@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.
- package/dist/bitcoin-base.d.ts +56 -0
- package/dist/bitcoin-base.d.ts.map +1 -0
- package/dist/bitcoin-base.js +138 -0
- package/dist/bitcoin-base.js.map +1 -0
- package/dist/bitcoin-interface-utils.d.ts +18 -0
- package/dist/bitcoin-interface-utils.d.ts.map +1 -0
- package/dist/bitcoin-interface-utils.js +31 -0
- package/dist/bitcoin-interface-utils.js.map +1 -0
- package/dist/bitcoin-interface.d.ts +154 -0
- package/dist/bitcoin-interface.d.ts.map +1 -0
- package/dist/bitcoin-interface.js +248 -0
- package/dist/bitcoin-interface.js.map +1 -0
- package/dist/bitcoin-utils.d.ts +70 -0
- package/dist/bitcoin-utils.d.ts.map +1 -0
- package/dist/bitcoin-utils.js +388 -0
- package/dist/bitcoin-utils.js.map +1 -0
- package/dist/helper/burn-request-helper.d.ts +7 -0
- package/dist/helper/burn-request-helper.d.ts.map +1 -0
- package/dist/helper/burn-request-helper.js +26 -0
- package/dist/helper/burn-request-helper.js.map +1 -0
- package/dist/helper/teleport-request-helper.d.ts +45 -0
- package/dist/helper/teleport-request-helper.d.ts.map +1 -0
- package/dist/helper/teleport-request-helper.js +141 -0
- package/dist/helper/teleport-request-helper.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/sign/sign-transaction.d.ts +8 -0
- package/dist/sign/sign-transaction.d.ts.map +1 -0
- package/dist/sign/sign-transaction.js +41 -0
- package/dist/sign/sign-transaction.js.map +1 -0
- package/dist/teleport-dao-payments.d.ts +92 -0
- package/dist/teleport-dao-payments.d.ts.map +1 -0
- package/dist/teleport-dao-payments.js +203 -0
- package/dist/teleport-dao-payments.js.map +1 -0
- package/dist/transaction-builder/bitcoin-transaction-builder.d.ts +12 -0
- package/dist/transaction-builder/bitcoin-transaction-builder.d.ts.map +1 -0
- package/dist/transaction-builder/bitcoin-transaction-builder.js +50 -0
- package/dist/transaction-builder/bitcoin-transaction-builder.js.map +1 -0
- package/dist/transaction-builder/transaction-builder-common.d.ts +80 -0
- package/dist/transaction-builder/transaction-builder-common.d.ts.map +1 -0
- package/dist/transaction-builder/transaction-builder-common.js +170 -0
- package/dist/transaction-builder/transaction-builder-common.js.map +1 -0
- package/dist/transaction-builder/transaction-builder.d.ts +19 -0
- package/dist/transaction-builder/transaction-builder.d.ts.map +1 -0
- package/dist/transaction-builder/transaction-builder.js +130 -0
- package/dist/transaction-builder/transaction-builder.js.map +1 -0
- package/dist/utils/networks.d.ts +36 -0
- package/dist/utils/networks.d.ts.map +1 -0
- package/dist/utils/networks.js +30 -0
- package/dist/utils/networks.js.map +1 -0
- package/dist/utils/tools.d.ts +13 -0
- package/dist/utils/tools.d.ts.map +1 -0
- package/dist/utils/tools.js +65 -0
- package/dist/utils/tools.js.map +1 -0
- package/package.json +34 -0
- package/src/bitcoin-base.js +174 -0
- package/src/bitcoin-interface-utils.js +42 -0
- package/src/bitcoin-interface.js +267 -0
- package/src/bitcoin-utils.js +443 -0
- package/src/helper/burn-request-helper.js +27 -0
- package/src/helper/teleport-request-helper.js +162 -0
- package/src/index.js +15 -0
- package/src/sign/sign-transaction.js +36 -0
- package/src/teleport-dao-payments.js +276 -0
- package/src/transaction-builder/bitcoin-transaction-builder.js +37 -0
- package/src/transaction-builder/transaction-builder-common.js +228 -0
- package/src/transaction-builder/transaction-builder.js +135 -0
- package/src/utils/networks.js +31 -0
- package/src/utils/tools.js +72 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
const bip39 = require("bip39")
|
|
2
|
+
let varUnit = require("varuint-bitcoin")
|
|
3
|
+
const fastRoot = require("merkle-lib/fastRoot")
|
|
4
|
+
const merkle = require("merkle-lib")
|
|
5
|
+
const merkleProof = require("merkle-lib/proof")
|
|
6
|
+
const bitcoin = require("bitcoinjs-lib")
|
|
7
|
+
const networks = require("./utils/networks")
|
|
8
|
+
|
|
9
|
+
function generateMnemonic() {
|
|
10
|
+
const mnemonic = bip39.generateMnemonic(256)
|
|
11
|
+
return mnemonic
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function parseRawTransaction(rawTransaction) {
|
|
15
|
+
const size = {
|
|
16
|
+
version: 4,
|
|
17
|
+
flag: 2,
|
|
18
|
+
tx: 32,
|
|
19
|
+
index: 4,
|
|
20
|
+
sequence: 4,
|
|
21
|
+
amount: 8,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let offset = 0
|
|
25
|
+
let version = rawTransaction.slice(offset, size.version * 2)
|
|
26
|
+
offset += size.version * 2
|
|
27
|
+
let flag = rawTransaction.slice(offset, offset + size.flag * 2)
|
|
28
|
+
offset = flag === "0001" ? offset + size.flag * 2 : offset // * 0x0001 is flag in segwit transactions
|
|
29
|
+
|
|
30
|
+
let inputsStartIndex = offset
|
|
31
|
+
|
|
32
|
+
let numberOfInputs = varUnit.decode(Buffer.from(rawTransaction.slice(offset), "hex"))
|
|
33
|
+
let noiSize = varUnit.encodingLength(numberOfInputs)
|
|
34
|
+
offset += noiSize * 2
|
|
35
|
+
|
|
36
|
+
for (let i = 0; i < numberOfInputs; i += 1) {
|
|
37
|
+
offset += size.tx * 2
|
|
38
|
+
offset += size.index * 2
|
|
39
|
+
let sigLength = varUnit.decode(Buffer.from(rawTransaction.slice(offset), "hex"))
|
|
40
|
+
let sigLengthSize = varUnit.encodingLength(sigLength)
|
|
41
|
+
offset += sigLengthSize * 2
|
|
42
|
+
offset += sigLength * 2
|
|
43
|
+
offset += size.sequence * 2
|
|
44
|
+
}
|
|
45
|
+
let inputLastIndex = offset
|
|
46
|
+
let outputStartIndex = offset
|
|
47
|
+
|
|
48
|
+
let numberOfOutputs = varUnit.decode(Buffer.from(rawTransaction.slice(offset), "hex"))
|
|
49
|
+
let nooSize = varUnit.encodingLength(numberOfOutputs)
|
|
50
|
+
offset += nooSize * 2
|
|
51
|
+
|
|
52
|
+
for (let i = 0; i < numberOfOutputs; i += 1) {
|
|
53
|
+
offset += size.amount * 2
|
|
54
|
+
let unlockSigLength = varUnit.decode(Buffer.from(rawTransaction.slice(offset), "hex"))
|
|
55
|
+
let unlockSigLengthSize = varUnit.encodingLength(unlockSigLength)
|
|
56
|
+
offset += unlockSigLengthSize * 2
|
|
57
|
+
offset += unlockSigLength * 2
|
|
58
|
+
}
|
|
59
|
+
let outputLastIndex = offset
|
|
60
|
+
|
|
61
|
+
version = `0x${version}`
|
|
62
|
+
flag = `0x${flag}`
|
|
63
|
+
const vin = `0x${rawTransaction.slice(inputsStartIndex, inputLastIndex)}`
|
|
64
|
+
const vout = `0x${rawTransaction.slice(outputStartIndex, outputLastIndex)}`
|
|
65
|
+
let witness = `0x${rawTransaction.slice(outputLastIndex, rawTransaction.length - 8)}`
|
|
66
|
+
let locktime = `0x${rawTransaction.slice(rawTransaction.length - 8, rawTransaction.length)}`
|
|
67
|
+
return { version, flag, vin, vout, witness, locktime }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function reverseBytes(hexInput) {
|
|
71
|
+
return Buffer.from(hexInput, "hex").reverse().toString("hex")
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getAddressType(address, network) {
|
|
75
|
+
if (address.startsWith(network.bech32)) {
|
|
76
|
+
// todo : check length - it could be p2wsh
|
|
77
|
+
return "p2wpkh"
|
|
78
|
+
}
|
|
79
|
+
let base58Data = bitcoin.address.fromBase58Check(address)
|
|
80
|
+
if (base58Data.version === Number(network.scriptHash)) {
|
|
81
|
+
return "p2sh"
|
|
82
|
+
}
|
|
83
|
+
if (base58Data.version === Number(network.pubKeyHash)) {
|
|
84
|
+
return "p2pkh"
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
throw new Error("invalid address")
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function createAddressObjectByHash({ addressType, hash }, network) {
|
|
91
|
+
let addressObject
|
|
92
|
+
switch (addressType) {
|
|
93
|
+
case "p2pkh":
|
|
94
|
+
addressObject = bitcoin.payments.p2pkh({
|
|
95
|
+
hash,
|
|
96
|
+
network,
|
|
97
|
+
})
|
|
98
|
+
break
|
|
99
|
+
case "p2wpkh":
|
|
100
|
+
addressObject = bitcoin.payments.p2wpkh({
|
|
101
|
+
hash,
|
|
102
|
+
network,
|
|
103
|
+
})
|
|
104
|
+
break
|
|
105
|
+
case "p2sh":
|
|
106
|
+
addressObject = bitcoin.payments.p2sh({
|
|
107
|
+
hash,
|
|
108
|
+
network,
|
|
109
|
+
})
|
|
110
|
+
break
|
|
111
|
+
default:
|
|
112
|
+
throw new Error("address type is incorrect")
|
|
113
|
+
}
|
|
114
|
+
return addressObject
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function createAddressObjectByScript({ addressType, script }, network) {
|
|
118
|
+
let addressObject
|
|
119
|
+
switch (addressType) {
|
|
120
|
+
case "p2pkh":
|
|
121
|
+
addressObject = bitcoin.payments.p2pkh({
|
|
122
|
+
output: script,
|
|
123
|
+
network,
|
|
124
|
+
})
|
|
125
|
+
break
|
|
126
|
+
case "p2wpkh":
|
|
127
|
+
addressObject = bitcoin.payments.p2wpkh({
|
|
128
|
+
output: script,
|
|
129
|
+
network,
|
|
130
|
+
})
|
|
131
|
+
break
|
|
132
|
+
case "p2sh":
|
|
133
|
+
addressObject = bitcoin.payments.p2sh({
|
|
134
|
+
output: script,
|
|
135
|
+
network,
|
|
136
|
+
})
|
|
137
|
+
break
|
|
138
|
+
default:
|
|
139
|
+
throw new Error("address type is incorrect")
|
|
140
|
+
}
|
|
141
|
+
return addressObject
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function createAddressObjectByPublicKey({ addressType, publicKey }, network) {
|
|
145
|
+
let addressObject
|
|
146
|
+
|
|
147
|
+
switch (addressType) {
|
|
148
|
+
case "p2pkh":
|
|
149
|
+
addressObject = bitcoin.payments.p2pkh({
|
|
150
|
+
pubkey: publicKey,
|
|
151
|
+
network,
|
|
152
|
+
})
|
|
153
|
+
break
|
|
154
|
+
case "p2wpkh":
|
|
155
|
+
addressObject = bitcoin.payments.p2wpkh({
|
|
156
|
+
pubkey: publicKey,
|
|
157
|
+
network,
|
|
158
|
+
})
|
|
159
|
+
break
|
|
160
|
+
case "p2sh-p2wpkh":
|
|
161
|
+
addressObject = bitcoin.payments.p2sh({
|
|
162
|
+
redeem: bitcoin.payments.p2wpkh({
|
|
163
|
+
pubkey: publicKey,
|
|
164
|
+
network,
|
|
165
|
+
}),
|
|
166
|
+
})
|
|
167
|
+
break
|
|
168
|
+
default:
|
|
169
|
+
throw new Error("address type is incorrect")
|
|
170
|
+
}
|
|
171
|
+
return addressObject
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function createAddressObjectByAddress(address, network) {
|
|
175
|
+
let addressType = getAddressType(address, network)
|
|
176
|
+
let addressObject
|
|
177
|
+
switch (addressType) {
|
|
178
|
+
case "p2pkh":
|
|
179
|
+
addressObject = bitcoin.payments.p2pkh({
|
|
180
|
+
address,
|
|
181
|
+
network,
|
|
182
|
+
})
|
|
183
|
+
break
|
|
184
|
+
case "p2wpkh":
|
|
185
|
+
addressObject = bitcoin.payments.p2wpkh({
|
|
186
|
+
address,
|
|
187
|
+
network,
|
|
188
|
+
})
|
|
189
|
+
break
|
|
190
|
+
case "p2sh":
|
|
191
|
+
addressObject = bitcoin.payments.p2sh({
|
|
192
|
+
address,
|
|
193
|
+
network,
|
|
194
|
+
})
|
|
195
|
+
break
|
|
196
|
+
default:
|
|
197
|
+
throw new Error("address type is incorrect")
|
|
198
|
+
}
|
|
199
|
+
return { addressObject, addressType }
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// not used
|
|
203
|
+
|
|
204
|
+
async function deriveAddressFromPubKey(pubKey, network) {
|
|
205
|
+
let { address } = this.bitcoinJS.payments.p2pkh({
|
|
206
|
+
network,
|
|
207
|
+
pubkey: Buffer.from(pubKey, "hex"),
|
|
208
|
+
})
|
|
209
|
+
return address
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function deriveAddressFromBufferPubKey(pubKey, network) {
|
|
213
|
+
let { address } = this.bitcoinJS.payments.p2pkh({ network, pubkey: pubKey })
|
|
214
|
+
return address
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async function getPubKeyFromPrivateKey(privateKey, network) {
|
|
218
|
+
let key = bitcoin.ECPair.fromWIF(privateKey, network)
|
|
219
|
+
return key.publicKey
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function calculateMerkleProof(blockTransactions, txId, blockMerkleRoot = undefined) {
|
|
223
|
+
let transactionIndex = blockTransactions.findIndex((tx) => tx === txId)
|
|
224
|
+
if (transactionIndex < 0) throw new Error("txId is not in this tree")
|
|
225
|
+
let data = blockTransactions.map((a) => Buffer.from(a, "hex").reverse())
|
|
226
|
+
|
|
227
|
+
if (
|
|
228
|
+
blockMerkleRoot &&
|
|
229
|
+
blockMerkleRoot !== fastRoot(data, bitcoin.crypto.hash256).toString("hex")
|
|
230
|
+
) {
|
|
231
|
+
throw new Error("calculated anf block merkleRoot not matched")
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
let tree = merkle(data, bitcoin.crypto.hash256)
|
|
235
|
+
let proof = merkleProof(tree, data[transactionIndex])
|
|
236
|
+
|
|
237
|
+
let intermediateNodesArray = proof
|
|
238
|
+
.map((_id) => _id && _id.toString("hex"))
|
|
239
|
+
.filter((_id) => _id != null)
|
|
240
|
+
let intermediateNodes = intermediateNodesArray.reduce(
|
|
241
|
+
(a, value, index) =>
|
|
242
|
+
index !== transactionIndex % 2 && index < intermediateNodesArray.length - 1 ? a + value : a,
|
|
243
|
+
"0x",
|
|
244
|
+
)
|
|
245
|
+
return {
|
|
246
|
+
intermediateNodes,
|
|
247
|
+
transactionIndex,
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function parseBlockHeader(headerHex) {
|
|
252
|
+
const size = {
|
|
253
|
+
version: 4,
|
|
254
|
+
previousBlockHash: 32,
|
|
255
|
+
merkleRoot: 32,
|
|
256
|
+
timestamp: 4,
|
|
257
|
+
difficulty: 4,
|
|
258
|
+
nonce: 4,
|
|
259
|
+
}
|
|
260
|
+
let offset = 0
|
|
261
|
+
let result = {}
|
|
262
|
+
for (let key in size) {
|
|
263
|
+
result[key] = headerHex.slice(offset, offset + size[key] * 2)
|
|
264
|
+
offset += size[key] * 2
|
|
265
|
+
}
|
|
266
|
+
return result
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function convertBitcoinScriptToAddress(script, network) {
|
|
270
|
+
try {
|
|
271
|
+
return bitcoin.address?.fromOutputScript(script, network)
|
|
272
|
+
} catch (error) {
|
|
273
|
+
return null
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function parseRawBlock(rawBlockHex, blockNumber = undefined, network = bitcoin.networks.network) {
|
|
278
|
+
let block = bitcoin.Block.fromBuffer(Buffer.from(rawBlockHex, "hex"))
|
|
279
|
+
let blockHash = block.getHash().toString("hex")
|
|
280
|
+
let merkleRoot = block.merkleRoot.toString("hex")
|
|
281
|
+
let prvBlockHash = block.prevHash.toString("hex")
|
|
282
|
+
return {
|
|
283
|
+
blockNumber,
|
|
284
|
+
merkleRoot,
|
|
285
|
+
prvBlockHash,
|
|
286
|
+
transactions: block.transactions.map((tx) => ({
|
|
287
|
+
txId: tx.getId(),
|
|
288
|
+
version: tx.version,
|
|
289
|
+
locktime: tx.locktime,
|
|
290
|
+
blockNumber,
|
|
291
|
+
blockHash,
|
|
292
|
+
vout: tx.outs.map((vo) => ({
|
|
293
|
+
address: convertBitcoinScriptToAddress(vo.script, network),
|
|
294
|
+
script: vo.script.toString("hex"),
|
|
295
|
+
value: vo.value,
|
|
296
|
+
})),
|
|
297
|
+
vin: tx.ins.map((vi) => ({
|
|
298
|
+
txId: vi.hash.reverse().toString("hex"),
|
|
299
|
+
index: vi.index,
|
|
300
|
+
// // todo optional get from utxo
|
|
301
|
+
// address: null,
|
|
302
|
+
// script: null,
|
|
303
|
+
// value: null,
|
|
304
|
+
})),
|
|
305
|
+
})),
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function extractTransactionsAndBlockInfoFromRawBlock(
|
|
310
|
+
rawBlockHex,
|
|
311
|
+
blockNumber,
|
|
312
|
+
addresses = [],
|
|
313
|
+
inputs = [],
|
|
314
|
+
network = bitcoin.networks.bitcoin,
|
|
315
|
+
) {
|
|
316
|
+
let block = bitcoin.Block.fromBuffer(Buffer.from(rawBlockHex, "hex"))
|
|
317
|
+
let blockHash = block.getHash().reverse().toString("hex")
|
|
318
|
+
let merkleRoot = block.merkleRoot.toString("hex")
|
|
319
|
+
let prvBlockHash = block.prevHash.toString("hex")
|
|
320
|
+
|
|
321
|
+
let blockInfo = {
|
|
322
|
+
blockNumber,
|
|
323
|
+
blockHash,
|
|
324
|
+
merkleRoot,
|
|
325
|
+
prvBlockHash,
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
let addressScript = addresses.map((address) =>
|
|
329
|
+
createAddressObjectByAddress(address, network).addressObject.output.toString("hex"),
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
let blockTxIds = block.transactions.map((tx) => tx.getId())
|
|
333
|
+
|
|
334
|
+
let withdrawTxs = []
|
|
335
|
+
let depositTxs = []
|
|
336
|
+
block.transactions.forEach((tx) => {
|
|
337
|
+
let txId = tx.getId()
|
|
338
|
+
let transactionAddressIndex
|
|
339
|
+
|
|
340
|
+
// todo : withdraw txs not working (we cant use utxo here)
|
|
341
|
+
let txVinInput = inputs.find((vin) => vin.txId === txId)
|
|
342
|
+
if (txVinInput) {
|
|
343
|
+
let txMerkleProof = calculateMerkleProof(blockTxIds, txId, merkleRoot)
|
|
344
|
+
|
|
345
|
+
withdrawTxs.push({
|
|
346
|
+
txId: tx.getId(),
|
|
347
|
+
version: tx.version,
|
|
348
|
+
locktime: tx.locktime,
|
|
349
|
+
blockNumber,
|
|
350
|
+
blockHash,
|
|
351
|
+
merkleProof: txMerkleProof,
|
|
352
|
+
vout: tx.outs.map((vo) => ({
|
|
353
|
+
address: convertBitcoinScriptToAddress(vo.script, network),
|
|
354
|
+
script: vo.script.toString("hex"),
|
|
355
|
+
value: vo.value,
|
|
356
|
+
})),
|
|
357
|
+
vin: tx.ins.map((vi) => {
|
|
358
|
+
let viInput = inputs.find((vin) => vin.txId === txId)
|
|
359
|
+
return {
|
|
360
|
+
txId: vi.hash.reverse().toString("hex"),
|
|
361
|
+
index: vi.index,
|
|
362
|
+
address: viInput.address || null,
|
|
363
|
+
script: viInput.script || null,
|
|
364
|
+
value: viInput.value || null,
|
|
365
|
+
}
|
|
366
|
+
}),
|
|
367
|
+
address: txVinInput.address,
|
|
368
|
+
addressScript: createAddressObjectByAddress(
|
|
369
|
+
txVinInput.address,
|
|
370
|
+
network,
|
|
371
|
+
).addressObject.output.toString("hex"),
|
|
372
|
+
})
|
|
373
|
+
} else if (
|
|
374
|
+
tx.outs.findIndex((blockTxVo) => {
|
|
375
|
+
let sIndex = addressScript.findIndex(
|
|
376
|
+
(addScript) => addScript === blockTxVo.script.toString("hex"),
|
|
377
|
+
)
|
|
378
|
+
if (sIndex >= 0) {
|
|
379
|
+
transactionAddressIndex = sIndex
|
|
380
|
+
return true
|
|
381
|
+
}
|
|
382
|
+
return false
|
|
383
|
+
}) >= 0
|
|
384
|
+
) {
|
|
385
|
+
// todo we can optimize this calculation (in following function, merkle tree calculate each times but we can do this once for this block)
|
|
386
|
+
let txMerkleProof = calculateMerkleProof(blockTxIds, txId, merkleRoot)
|
|
387
|
+
depositTxs.push({
|
|
388
|
+
txId: tx.getId(),
|
|
389
|
+
version: tx.version,
|
|
390
|
+
locktime: tx.locktime,
|
|
391
|
+
blockNumber,
|
|
392
|
+
blockHash,
|
|
393
|
+
merkleProof: txMerkleProof,
|
|
394
|
+
vout: tx.outs.map((vo) => ({
|
|
395
|
+
address: convertBitcoinScriptToAddress(vo.script, network),
|
|
396
|
+
script: vo.script.toString("hex"),
|
|
397
|
+
value: vo.value,
|
|
398
|
+
})),
|
|
399
|
+
vin: tx.ins.map((vi) => ({
|
|
400
|
+
txId: vi.hash.reverse().toString("hex"),
|
|
401
|
+
index: vi.index,
|
|
402
|
+
// // todo optional get from utxo
|
|
403
|
+
// address: null,
|
|
404
|
+
// script: null,
|
|
405
|
+
// value: null,
|
|
406
|
+
})),
|
|
407
|
+
addressScript: addressScript[transactionAddressIndex],
|
|
408
|
+
address: addresses[transactionAddressIndex],
|
|
409
|
+
})
|
|
410
|
+
}
|
|
411
|
+
})
|
|
412
|
+
return {
|
|
413
|
+
blockInfo,
|
|
414
|
+
withdrawTxs,
|
|
415
|
+
depositTxs,
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
module.exports = {
|
|
420
|
+
parseRawTransaction,
|
|
421
|
+
calculateMerkleProof,
|
|
422
|
+
createAddressObjectByHash,
|
|
423
|
+
createAddressObjectByPublicKey,
|
|
424
|
+
createAddressObjectByAddress,
|
|
425
|
+
createAddressObjectByScript,
|
|
426
|
+
|
|
427
|
+
// ------------------------
|
|
428
|
+
getAddressType,
|
|
429
|
+
deriveAddressFromPubKey,
|
|
430
|
+
deriveAddressFromBufferPubKey,
|
|
431
|
+
getPubKeyFromPrivateKey,
|
|
432
|
+
reverseBytes,
|
|
433
|
+
|
|
434
|
+
parseBlockHeader,
|
|
435
|
+
generateMnemonic,
|
|
436
|
+
|
|
437
|
+
// ---------------------------
|
|
438
|
+
parseRawBlock,
|
|
439
|
+
extractTransactionsAndBlockInfoFromRawBlock,
|
|
440
|
+
|
|
441
|
+
// -----------------------------
|
|
442
|
+
networks,
|
|
443
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
function getBurnTransactionInfo(address, vin = [], vouts = []) {
|
|
2
|
+
let lockerVinIndex = vin.findIndex((vi) => vi.address === address)
|
|
3
|
+
if (lockerVinIndex >= 0) {
|
|
4
|
+
let lockerVin = vin[lockerVinIndex]
|
|
5
|
+
lockerVin.vinIndex = lockerVinIndex
|
|
6
|
+
let totalInputValue = vin.reduce((acc, current) => +acc + +current.value, 0)
|
|
7
|
+
let receivers = []
|
|
8
|
+
let changes = []
|
|
9
|
+
for (let i in vouts) {
|
|
10
|
+
let vout = {
|
|
11
|
+
...vouts[i],
|
|
12
|
+
index: i,
|
|
13
|
+
}
|
|
14
|
+
if (vout.address === address) {
|
|
15
|
+
changes.push(vout)
|
|
16
|
+
} else {
|
|
17
|
+
receivers.push(vout)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return { receivers, changes, totalInputValue, lockerVin }
|
|
21
|
+
}
|
|
22
|
+
return null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = {
|
|
26
|
+
getBurnTransactionInfo,
|
|
27
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
const { requestTypes: teleportRequestsType } = require("@teleportdao/configs").teleswap
|
|
2
|
+
|
|
3
|
+
function parseTeleportAndExchangeRequest(data) {
|
|
4
|
+
let parsedData = {}
|
|
5
|
+
parsedData.requestType = "transfer"
|
|
6
|
+
|
|
7
|
+
let offset = 0
|
|
8
|
+
parsedData.chainId = Number(`0x${data.slice(offset, (offset += 2))}`) // 1 bytes
|
|
9
|
+
parsedData.appId = Number(`0x${data.slice(offset, (offset += 4))}`) // 2 bytes
|
|
10
|
+
parsedData.recipientAddress = `0x${data.slice(offset, (offset += 40))}` // 20 bytes
|
|
11
|
+
parsedData.percentageFee = Number(`0x${data.slice(offset, (offset += 4))}`) / 100 // 2 bytes
|
|
12
|
+
parsedData.speed = data.slice(offset, (offset += 2)) === "01" // 1 byte
|
|
13
|
+
if (data.length === offset) {
|
|
14
|
+
return {
|
|
15
|
+
status: true,
|
|
16
|
+
data: parsedData,
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
parsedData.requestType = "exchange"
|
|
21
|
+
parsedData.exchangeTokenAddress = `0x${data.slice(offset, (offset += 40))}` // 20 bytes
|
|
22
|
+
parsedData.outputAmount = Number(`0x${data.slice(offset, (offset += 56))}`) // 28 bytes
|
|
23
|
+
parsedData.deadline = Number(`0x${data.slice(offset, (offset += 8))}`) // 4 bytes
|
|
24
|
+
parsedData.isFixedToken = data.slice(offset, (offset += 2)) === "01" // 1 byte
|
|
25
|
+
if (data.length === offset) {
|
|
26
|
+
return {
|
|
27
|
+
status: true,
|
|
28
|
+
data: parsedData,
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
status: false,
|
|
34
|
+
message: `invalid OP_RETURN data for requestType: 'transfer or exchange'. invalid data length : ${data.length} - valid length : ${offset}`,
|
|
35
|
+
code: "INVALID_OP_RETURN",
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function parseLendAndBorrowRequest(data) {
|
|
40
|
+
let parsedData = {}
|
|
41
|
+
parsedData.requestType = "lend"
|
|
42
|
+
|
|
43
|
+
let offset = 0
|
|
44
|
+
parsedData.chainId = Number(`0x${data.slice(offset, (offset += 2))}`) // 1 bytes
|
|
45
|
+
parsedData.appId = Number(`0x${data.slice(offset, (offset += 4))}`) // 2 bytes
|
|
46
|
+
parsedData.recipientAddress = `0x${data.slice(offset, (offset += 40))}` // 20 bytes
|
|
47
|
+
parsedData.percentageFee = Number(`0x${data.slice(offset, (offset += 4))}`) / 100 // 2 bytes
|
|
48
|
+
parsedData.mode = Number(`0x${data.slice(offset, (offset += 2))}`) // 1 byte
|
|
49
|
+
if (data.length === offset) {
|
|
50
|
+
return {
|
|
51
|
+
status: true,
|
|
52
|
+
data: parsedData,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
parsedData.requestType = "borrow"
|
|
56
|
+
parsedData.tokenAddress = `0x${data.slice(offset, (offset += 40))}` // 20 bytes
|
|
57
|
+
parsedData.borrowAmount = Number(`0x${data.slice(offset, (offset += 56))}`) // 28 bytes
|
|
58
|
+
if (data.length === offset) {
|
|
59
|
+
return {
|
|
60
|
+
status: true,
|
|
61
|
+
data: parsedData,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
status: false,
|
|
66
|
+
message: `invalid OP_RETURN data for requestType: 'lend or borrow'. invalid data length : ${data.length} - valid length : ${offset}`,
|
|
67
|
+
code: "INVALID_OP_RETURN",
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function parseRawRequest(opReturnData) {
|
|
72
|
+
let data = opReturnData.slice(2, 4) === "4c" ? opReturnData.slice(6) : opReturnData.slice(4)
|
|
73
|
+
|
|
74
|
+
// eslint-disable-next-line no-unused-vars
|
|
75
|
+
let _chainId = Number(`0x${data.slice(0, 2)}`) // 1 bytes
|
|
76
|
+
let appIdHex = data.slice(2, 6) // 2 bytes
|
|
77
|
+
if (!appIdHex) {
|
|
78
|
+
return {
|
|
79
|
+
status: false,
|
|
80
|
+
message: `invalid OP_RETURN data : ${data}`,
|
|
81
|
+
code: "INVALID_APP_ID",
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let appId = Number(`0x${appIdHex}`) // 2 bytes
|
|
86
|
+
|
|
87
|
+
// get type base on appId
|
|
88
|
+
let requestType = teleportRequestsType.find(
|
|
89
|
+
(rs) => appId >= rs.appIdRange[0] && appId <= rs.appIdRange[1],
|
|
90
|
+
)?.type
|
|
91
|
+
|
|
92
|
+
switch (requestType) {
|
|
93
|
+
case "teleport":
|
|
94
|
+
case "exchange":
|
|
95
|
+
return parseTeleportAndExchangeRequest(data)
|
|
96
|
+
case "lend":
|
|
97
|
+
return parseLendAndBorrowRequest(data)
|
|
98
|
+
default:
|
|
99
|
+
return {
|
|
100
|
+
status: false,
|
|
101
|
+
message: `invalid appId : ${appId}`,
|
|
102
|
+
code: "INVALID_OP_RETURN",
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function validateRequestAndValue(data, value, { minTeleporterFeeAmount = 0 }) {
|
|
108
|
+
if (!data) {
|
|
109
|
+
return {
|
|
110
|
+
status: false,
|
|
111
|
+
message: "no data to validate. it should not happen",
|
|
112
|
+
code: "NOT_ACCEPTED_BY_TELEPORTER",
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (+data.percentageFee > 100) {
|
|
117
|
+
return {
|
|
118
|
+
status: false,
|
|
119
|
+
message: `percentageFee greater than 100 is invalid. percentageFee: ${data.percentageFee}`,
|
|
120
|
+
code: "INVALID_FEE",
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if ((data.percentageFee / 100) * +value <= minTeleporterFeeAmount) {
|
|
125
|
+
return {
|
|
126
|
+
status: false,
|
|
127
|
+
message: `fee amount is less than or equal minimum teleporter fee amount.percentageFee: ${
|
|
128
|
+
data.percentageFee
|
|
129
|
+
} - value: ${value} - feeAmount ${((data.percentageFee / 100) * +value).toFixed(
|
|
130
|
+
8,
|
|
131
|
+
)} - minimumFee: ${minTeleporterFeeAmount}`,
|
|
132
|
+
code: "NOT_ACCEPTED_BY_TELEPORTER",
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
status: true,
|
|
138
|
+
data,
|
|
139
|
+
value,
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function checkAndParseProtocolRequest(vouts, address, config = { minTeleporterFeeAmount: 0 }) {
|
|
144
|
+
let opReturnData = vouts.find((vout_) => vout_.script.startsWith("6a"))?.script || null
|
|
145
|
+
if (opReturnData) {
|
|
146
|
+
let value = vouts.find((vout_) => vout_.address === address)?.value || 0
|
|
147
|
+
let dataResponse = parseRawRequest(opReturnData)
|
|
148
|
+
if (dataResponse.status) {
|
|
149
|
+
return validateRequestAndValue(dataResponse.data, value, config)
|
|
150
|
+
}
|
|
151
|
+
return dataResponse
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
status: false,
|
|
155
|
+
message: "transaction outputs should contain OP_RETURN",
|
|
156
|
+
code: "NO_OP_RETURN",
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
module.exports = {
|
|
161
|
+
checkAndParseProtocolRequest,
|
|
162
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// eslint-disable-next-line global-require
|
|
2
|
+
global.Buffer = global.Buffer || require("buffer").Buffer
|
|
3
|
+
const TeleportDaoPayment = require("./teleport-dao-payments")
|
|
4
|
+
const bitcoinUtils = require("./bitcoin-utils")
|
|
5
|
+
const BitcoinInterface = require("./bitcoin-interface")
|
|
6
|
+
const BitcoinInterfaceUtils = require("./bitcoin-interface-utils")
|
|
7
|
+
const BitcoinBase = require("./bitcoin-base")
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
TeleportDaoPayment,
|
|
11
|
+
BitcoinInterface,
|
|
12
|
+
BitcoinInterfaceUtils,
|
|
13
|
+
bitcoinUtils,
|
|
14
|
+
BitcoinBase,
|
|
15
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const bitcoin = require("bitcoinjs-lib")
|
|
2
|
+
|
|
3
|
+
class BitcoinLikeSignTransaction {
|
|
4
|
+
constructor(network) {
|
|
5
|
+
this.network = network
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
async signPsbt(extendedUnsignedTransaction, privateKey) {
|
|
9
|
+
const { network } = this
|
|
10
|
+
const keyPair = bitcoin.ECPair.fromPrivateKey(privateKey, {
|
|
11
|
+
network,
|
|
12
|
+
compressed: true,
|
|
13
|
+
})
|
|
14
|
+
const psbt = bitcoin.Psbt.fromBase64(extendedUnsignedTransaction.unsignedTransaction, {
|
|
15
|
+
network,
|
|
16
|
+
})
|
|
17
|
+
psbt.signAllInputs(keyPair)
|
|
18
|
+
|
|
19
|
+
const partialSigendPsbt = psbt.toBase64()
|
|
20
|
+
return partialSigendPsbt
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
finalizePsbts(psbtsBase64 = []) {
|
|
24
|
+
const finals = psbtsBase64.map((psbtBase64) =>
|
|
25
|
+
bitcoin.Psbt.fromBase64(psbtBase64, { network: this.network }),
|
|
26
|
+
)
|
|
27
|
+
const psbt =
|
|
28
|
+
finals.length === 1
|
|
29
|
+
? finals[0]
|
|
30
|
+
: new bitcoin.Psbt({ network: this.network }).combine(...finals)
|
|
31
|
+
psbt.finalizeAllInputs()
|
|
32
|
+
return psbt.extractTransaction().toHex()
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = BitcoinLikeSignTransaction
|