@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
@@ -1,6 +1,5 @@
1
- import TransactionBuilder, {
2
- BitcoinConnectionInfo,
3
- } from "./transaction-builder/bitcoin-transaction-builder"
1
+ import { BitcoinTransactionBuilder } from "./transaction-builder"
2
+ import type { BitcoinConnectionInfo } from "./type"
4
3
 
5
4
  import type { ExtendedUtxo, SignerInfo, Target } from "./transaction-builder/transaction-builder"
6
5
  import BitcoinSign from "./sign/sign-transaction"
@@ -9,7 +8,7 @@ import * as bip39 from "bip39"
9
8
  import { hdWalletPath } from "@teleportdao/configs"
10
9
  import { getPubKeyFromPrivateKeyHex } from "./bitcoin-utils"
11
10
  import networks from "./utils/networks"
12
- import { Network, Payment, Psbt } from "bitcoinjs-lib"
11
+ import { Network, Payment } from "bitcoinjs-lib"
13
12
  import { BitcoinInterface } from "./bitcoin-interface"
14
13
 
15
14
  import BIP32Factory from "bip32"
@@ -17,7 +16,7 @@ import ecc from "@bitcoinerlab/secp256k1"
17
16
 
18
17
  const bip32 = BIP32Factory(ecc)
19
18
 
20
- class BitcoinBase {
19
+ export class BitcoinBaseWallet {
21
20
  network: Network
22
21
  hdWalletPath: {
23
22
  p2pkh: string
@@ -28,7 +27,7 @@ class BitcoinBase {
28
27
  "p2sh-p2wsh": string
29
28
  p2tr: string
30
29
  }
31
- transactionBuilder: TransactionBuilder
30
+ transactionBuilder: BitcoinTransactionBuilder
32
31
  btcInterface: BitcoinInterface
33
32
  signer: BitcoinSign
34
33
  currentAccount?: string
@@ -42,7 +41,6 @@ class BitcoinBase {
42
41
  networkName: string,
43
42
  connectionInfo: BitcoinConnectionInfo = {
44
43
  api: {
45
- enabled: true,
46
44
  provider: "BlockStream",
47
45
  },
48
46
  },
@@ -50,7 +48,11 @@ class BitcoinBase {
50
48
  this.network = networks[networkName]
51
49
  this.hdWalletPath = hdWalletPath.bitcoin
52
50
 
53
- this.transactionBuilder = new TransactionBuilder(connectionInfo, networkName, this.network)
51
+ this.transactionBuilder = new BitcoinTransactionBuilder(
52
+ connectionInfo,
53
+ networkName,
54
+ this.network,
55
+ )
54
56
  this.btcInterface = this.transactionBuilder.btcInterface
55
57
 
56
58
  this.signer = new BitcoinSign(this.network)
@@ -126,6 +128,8 @@ class BitcoinBase {
126
128
 
127
129
  // todo : not completed
128
130
  setMultiSigAccount(accountType = "p2sh") {
131
+ throw new Error("not supported yet")
132
+
129
133
  /* eslint-disable no-unreachable */
130
134
  // todo : not completed
131
135
  switch (accountType) {
@@ -150,10 +154,6 @@ class BitcoinBase {
150
154
  this.publicKey = publicKey
151
155
  }
152
156
 
153
- setAccountPublicKey(publicKeyHex: string) {
154
- this.publicKey = Buffer.from(publicKeyHex, "hex")
155
- }
156
-
157
157
  setAccountPrivateKeyByMnemonic({
158
158
  mnemonic,
159
159
  mnemonicPassword = "",
@@ -182,6 +182,10 @@ class BitcoinBase {
182
182
  return this.setAccount(addressType)
183
183
  }
184
184
 
185
+ setAccountPublicKey(publicKeyHex: string) {
186
+ this.publicKey = Buffer.from(publicKeyHex, "hex")
187
+ }
188
+
185
189
  setAccount(accountType = "p2pkh") {
186
190
  if (!this.publicKey) {
187
191
  throw new Error("account not initialized")
@@ -197,6 +201,8 @@ class BitcoinBase {
197
201
  return addressObj.address
198
202
  }
199
203
 
204
+ //
205
+
200
206
  async getExtendedUtxo(input: SignerInfo) {
201
207
  return this.transactionBuilder.getExtendedUtxo(input)
202
208
  }
@@ -266,7 +272,7 @@ class BitcoinBase {
266
272
  changeAddress: string,
267
273
  staticFeeRate?: number,
268
274
  ) {
269
- let transaction = await this.btcInterface.getTransaction(txId)
275
+ let transaction = await this.btcInterface.apiProvider.getTransaction(txId)
270
276
 
271
277
  let extendedUtxo = transaction.vin.map((vi) => ({
272
278
  signerInfo: signerInfos.find((s) => s.address === vi.address)!,
@@ -303,7 +309,7 @@ class BitcoinBase {
303
309
  targets,
304
310
  feeRate,
305
311
  changeAddress: changeIndex >= 0 ? transaction.vout[changeIndex].address : changeAddress,
312
+ selectType: "inOrder",
306
313
  })
307
314
  }
308
315
  }
309
- export { BitcoinBase }
@@ -0,0 +1,181 @@
1
+ import BigNumber from "bignumber.js"
2
+ import { brc20 } from "@teleportdao/configs"
3
+
4
+ export type WrapOpReturn = {
5
+ chainId: number
6
+ appId: number
7
+ brc20TokenId: number
8
+ recipientAddress: string
9
+ inputAmount: string
10
+ outputAmount?: string
11
+ outputToken?: string
12
+ thirdPartyId?: number
13
+ }
14
+ export type WrapOpReturnAndType = WrapOpReturn & {
15
+ requestType: "wrap" | "wrapAndExchange"
16
+ }
17
+
18
+ const brc20WrapRequests = brc20.requestAppId
19
+
20
+ export function generateBrc2OpReturn({
21
+ chainId,
22
+ appId,
23
+ brc20TokenId,
24
+ inputAmount,
25
+ recipientAddress,
26
+ thirdPartyId = 0,
27
+ isExchange = false,
28
+ outputToken,
29
+ outputAmount,
30
+ }: WrapOpReturn & {
31
+ isExchange?: boolean
32
+ }) {
33
+ let data = ""
34
+ if (chainId.toString(16).length > 4) throw new Error("chainId should be less than 2 bytes")
35
+ data += chainId.toString(16).padStart(4, "0") // 2 bytes
36
+ data += appId.toString(16).padStart(2, "0") // 1 bytes
37
+ data += brc20TokenId.toString(16).padStart(4, "0") // 2 bytes
38
+ data += BigNumber(inputAmount).toString(16).padStart(26, "0") // 13 bytes
39
+ data += recipientAddress.replace("0x", "").toLowerCase().padStart(40, "0") // 20 bytes
40
+ data += BigNumber(thirdPartyId).toString(16).padStart(2, "0") // 1 bytes
41
+ if (isExchange) {
42
+ if (!outputAmount || !outputToken) {
43
+ throw new Error("outputAmount and outputToken are required for exchange")
44
+ }
45
+ data += outputToken.replace("0x", "").toLowerCase().padStart(40, "0") // 20 bytes
46
+ data += BigNumber(outputAmount).toString(16).padStart(26, "0") // 13 bytes
47
+ }
48
+ return data
49
+ }
50
+
51
+ export function parseBrc20OpReturn(data: string) {
52
+ let parsedData: any = {}
53
+ parsedData.chainId = Number(`0x${data.slice(0, 4)}`) // 2 bytes
54
+ parsedData.appId = Number(`0x${data.slice(4, 6)}`) // 1 bytes
55
+ parsedData.brc20TokenId = Number(`0x${data.slice(6, 10)}`) // 2 bytes
56
+ parsedData.inputAmount = new BigNumber(`0x${data.slice(10, 36)}`).toFixed(0) // 11 bytes
57
+ parsedData.recipientAddress = `0x${data.slice(36, 76)}` // 20 bytes
58
+ parsedData.thirdPartyId = `0x${data.slice(76, 78)}` // 20 bytes
59
+ if (data.length === 78) {
60
+ parsedData.requestType = "wrap"
61
+ return {
62
+ status: true,
63
+ data: parsedData as WrapOpReturnAndType,
64
+ }
65
+ }
66
+ parsedData.outputToken = `0x${data.slice(78, 118)}` // 20 bytes
67
+ parsedData.outputAmount = new BigNumber(`0x${data.slice(118, 144)}`).toFixed(0) // 20 bytes
68
+ if (data.length === 144) {
69
+ parsedData.requestType = "swapAndWrap"
70
+ return {
71
+ status: true,
72
+ data: parsedData as WrapOpReturnAndType,
73
+ }
74
+ }
75
+
76
+ return {
77
+ status: false,
78
+ message: `invalid OP_RETURN data for requestType: 'wrap or exchange'. invalid data length : ${data.length} - valid length : 144 , 78`,
79
+ code: "INVALID_OP_RETURN",
80
+ }
81
+ }
82
+
83
+ function parseBrc20RawRequest(opReturnData: string): {
84
+ status: boolean
85
+ data?: WrapOpReturnAndType
86
+ message?: string
87
+ code?: string
88
+ } {
89
+ let data = opReturnData.slice(2, 4) === "4c" ? opReturnData.slice(6) : opReturnData.slice(4)
90
+ // eslint-disable-next-line no-unused-vars
91
+ let appIdHex = data.slice(4, 6) // 2 bytes
92
+ if (!appIdHex) {
93
+ return {
94
+ status: false,
95
+ message: `invalid OP_RETURN data : ${data}`,
96
+ code: "INVALID_APP_ID",
97
+ }
98
+ }
99
+
100
+ let appId = Number(`0x${appIdHex}`) // 2 bytes
101
+
102
+ // get type base on appId
103
+ let requestType = Object.keys(brc20WrapRequests).find(
104
+ (key) =>
105
+ appId >= brc20WrapRequests[key as keyof typeof brc20WrapRequests].appIdRange[0] &&
106
+ appId <= brc20WrapRequests[key as keyof typeof brc20WrapRequests].appIdRange[1],
107
+ )
108
+
109
+ switch (requestType) {
110
+ case "wrap":
111
+ case "wrapAndExchange":
112
+ return parseBrc20OpReturn(data)
113
+ default:
114
+ return {
115
+ status: false,
116
+ message: `invalid appId : ${appId}`,
117
+ code: "INVALID_OP_RETURN",
118
+ }
119
+ }
120
+ }
121
+
122
+ export function checkAndParseBrc20Request(
123
+ vouts: {
124
+ address?: string
125
+ script: string
126
+ value: number
127
+ }[],
128
+ address: string,
129
+ ): {
130
+ status: boolean
131
+ data?: WrapOpReturnAndType
132
+ dataOutputIndex?: number
133
+ lockerFeeValue?: number
134
+ lockerFeeOutputIndex?: number
135
+ brc20OutputIndex?: number
136
+ brc20OutputValue?: number
137
+ message?: string
138
+ code?: string
139
+ } {
140
+ let requestOutputIndex = vouts.findIndex((vout_) => vout_.script.startsWith("6a"))
141
+ if (requestOutputIndex >= 0) {
142
+ let opReturnData = vouts[requestOutputIndex]?.script
143
+ if (!opReturnData) {
144
+ return {
145
+ status: false,
146
+ message: "invalid OP_RETURN",
147
+ code: "INVALID_OP_RETURN",
148
+ }
149
+ }
150
+
151
+ let brc20Transfer = vouts[0]
152
+ if (brc20Transfer.address !== address || brc20Transfer.value < 546) {
153
+ return {
154
+ status: false,
155
+ message: "no brc20 transfer output",
156
+ code: "NO_BRC20_TRANSFER",
157
+ }
158
+ }
159
+
160
+ let dataResponse = parseBrc20RawRequest(opReturnData)
161
+ if (dataResponse.status) {
162
+ let lockerFeeOutputIndex = vouts.findIndex((vout_, i) => vout_.address === address && +i > 0)
163
+ let lockerFeeValue = lockerFeeOutputIndex >= 0 ? vouts[lockerFeeOutputIndex].value : 0
164
+ return {
165
+ status: true,
166
+ data: dataResponse.data!,
167
+ dataOutputIndex: requestOutputIndex,
168
+ lockerFeeValue,
169
+ lockerFeeOutputIndex,
170
+ brc20OutputIndex: 0,
171
+ brc20OutputValue: brc20Transfer.value,
172
+ }
173
+ }
174
+ return dataResponse
175
+ }
176
+ return {
177
+ status: false,
178
+ message: "transaction outputs should contain OP_RETURN",
179
+ code: "NO_OP_RETURN",
180
+ }
181
+ }
@@ -0,0 +1,3 @@
1
+ export * as brc20Helper from "./brc20-helper"
2
+ export * as teleswapHelper from "./teleswap-helper"
3
+ export * as ordinalHelper from "./ordinal-helper"
@@ -0,0 +1,118 @@
1
+ import * as bitcoin from "bitcoinjs-lib"
2
+ import * as ecc from "tiny-secp256k1"
3
+
4
+ bitcoin.initEccLib(ecc)
5
+
6
+ // eslint-disable-next-line consistent-return
7
+ export function createAddressObjectByScriptNoType(
8
+ script: Buffer,
9
+ network: bitcoin.Network = bitcoin.networks.bitcoin,
10
+ ) {
11
+ let addressObject: bitcoin.payments.Payment
12
+
13
+ try {
14
+ addressObject = bitcoin.payments.p2pkh({
15
+ output: script,
16
+ network,
17
+ })
18
+ if (addressObject.address) {
19
+ return {
20
+ addressObject,
21
+ addressType: "p2pkh",
22
+ }
23
+ }
24
+ } catch (err) {
25
+ /* empty */
26
+ }
27
+
28
+ try {
29
+ addressObject = bitcoin.payments.p2wpkh({
30
+ output: script,
31
+ network,
32
+ })
33
+ if (addressObject.address) {
34
+ return {
35
+ addressObject,
36
+ addressType: "p2wpkh",
37
+ }
38
+ }
39
+ } catch (err) {
40
+ /* empty */
41
+ }
42
+
43
+ try {
44
+ addressObject = bitcoin.payments.p2sh({
45
+ output: script,
46
+ network,
47
+ })
48
+ if (addressObject.address) {
49
+ return {
50
+ addressObject,
51
+ addressType: "p2sh",
52
+ }
53
+ }
54
+ } catch (err) {
55
+ /* empty */
56
+ }
57
+
58
+ try {
59
+ addressObject = bitcoin.payments.p2wsh({
60
+ output: script,
61
+ network,
62
+ })
63
+ if (addressObject.address) {
64
+ return {
65
+ addressObject,
66
+ addressType: "p2wsh",
67
+ }
68
+ }
69
+ } catch (err) {
70
+ /* empty */
71
+ }
72
+
73
+ try {
74
+ addressObject = bitcoin.payments.p2tr({
75
+ output: script,
76
+ network,
77
+ })
78
+ if (addressObject.address) {
79
+ return {
80
+ addressObject,
81
+ addressType: "p2tr",
82
+ }
83
+ }
84
+ } catch (err) {
85
+ /* empty */
86
+ }
87
+ }
88
+
89
+ export function splitBuffer(buffer: Buffer, partLength: number) {
90
+ const parts: Buffer[] = []
91
+ for (let i = 0; i < buffer.length; i += partLength) {
92
+ const part = buffer.subarray(i, i + partLength)
93
+ parts.push(part)
94
+ }
95
+ return parts
96
+ }
97
+
98
+ export function getOrdinalScript(
99
+ file: { buffer: Buffer; type: string },
100
+ internalPublicKeyHex: string,
101
+ ) {
102
+ let bufferParts = splitBuffer(file.buffer, 520)
103
+ const splittedFileHex = bufferParts.map((part) => part.toString("hex")).join(" ")
104
+ const fileTypeHex = Buffer.from(file.type!, "utf8").toString("hex")
105
+ const ORDINAL_ORD_HEX = Buffer.from("ord", "utf8").toString("hex")
106
+ const script = `${internalPublicKeyHex} OP_CHECKSIG OP_FALSE OP_IF ${ORDINAL_ORD_HEX} 00000000000000000000 ${fileTypeHex} OP_0 ${splittedFileHex} OP_ENDIF`
107
+
108
+ // we add 00000000000000000000 so we can modify INCORRECT OPCODE
109
+ const leafScript = Buffer.from(
110
+ bitcoin.script.fromASM(script).toString("hex").replace("0a00000000000000000000", "0101"),
111
+ "hex",
112
+ )
113
+ return leafScript
114
+ }
115
+
116
+ export function toXOnly(pubKey: Buffer) {
117
+ return pubKey.length === 32 ? pubKey : pubKey.subarray(1, 33)
118
+ }
@@ -0,0 +1,300 @@
1
+ import { teleswap } from "@teleportdao/configs"
2
+ import BigNumber from "bignumber.js"
3
+
4
+ type Vin = {
5
+ address: string
6
+ value: number
7
+ }
8
+
9
+ type Vout = {
10
+ address?: string
11
+ value: number
12
+ script: string
13
+ }
14
+
15
+ export type WrapOpReturn = {
16
+ chainId: number
17
+ appId: number
18
+ recipientAddress: string
19
+ percentageFee: number
20
+ speed: boolean
21
+ //
22
+ outputToken?: string
23
+ outputAmount?: string
24
+ deadline?: string
25
+ isFixedToken?: boolean
26
+ }
27
+
28
+ export type UnwrapInfo = {
29
+ receivers: (Vout & {
30
+ index: number
31
+ })[]
32
+ changes: (Vout & {
33
+ index: number
34
+ })[]
35
+ totalInputValue: number
36
+ lockerVin: {
37
+ vinIndex: number
38
+ address: string
39
+ value: number
40
+ }
41
+ }
42
+
43
+ export type WrapOpReturnWithType = WrapOpReturn & {
44
+ requestType: "wrap" | "wrapAndSwap"
45
+ }
46
+
47
+ const wrapOpReturnLength = {
48
+ chainId: 2,
49
+ appId: 4,
50
+ percentageFee: 4,
51
+ outputAmount: 56,
52
+ deadline: 8,
53
+ }
54
+
55
+ export function generateWrapOpReturn({
56
+ chainId,
57
+ appId,
58
+ recipientAddress, // 20 bytes
59
+ percentageFee, // 2 bytes in percent %
60
+ speed = false, // 1 byte
61
+ isExchange = false,
62
+ outputToken, // 20 bytes
63
+ outputAmount, // 28 bytes
64
+ deadline, // 4 bytes
65
+ isFixedToken = false, // 1 byte
66
+ }: WrapOpReturn & {
67
+ isExchange?: boolean
68
+ }) {
69
+ let data = ""
70
+ if (BigNumber(chainId).toString(16).length > wrapOpReturnLength.chainId) {
71
+ throw new Error("invalid chainId")
72
+ }
73
+ if (BigNumber(appId).toString(16).length > wrapOpReturnLength.appId) {
74
+ throw new Error("invalid appId")
75
+ }
76
+ if (BigNumber(percentageFee).toString(16).length > wrapOpReturnLength.percentageFee) {
77
+ throw new Error("invalid percentageFee")
78
+ }
79
+ data += BigNumber(chainId).toString(16).padStart(2, "0")
80
+ data += BigNumber(appId).toString(16).padStart(4, "0")
81
+ data += recipientAddress.replace("0x", "").toLowerCase().padStart(40, "0")
82
+ data += BigNumber((percentageFee * 100).toFixed(0))
83
+ .toString(16)
84
+ .padStart(4, "0")
85
+ data += speed ? "01" : "00"
86
+ if (!isExchange) {
87
+ if (data.length !== 26 * 2) throw new Error("invalid data length")
88
+ return data
89
+ }
90
+ if (!outputToken) {
91
+ throw new Error("invalid outputToken")
92
+ }
93
+ if (
94
+ !outputAmount ||
95
+ BigNumber(outputAmount).toString(16).length > wrapOpReturnLength.outputAmount
96
+ ) {
97
+ throw new Error("invalid outputAmount")
98
+ }
99
+ if (!deadline || BigNumber(deadline).toString(16).length > wrapOpReturnLength.deadline) {
100
+ throw new Error("invalid deadline")
101
+ }
102
+
103
+ data += outputToken.replace("0x", "").toLowerCase().padStart(40, "0")
104
+ data += BigNumber(outputAmount).toString(16).padStart(56, "0")
105
+ data += BigNumber(deadline).toString(16).padStart(8, "0")
106
+ data += isFixedToken ? "01" : "00"
107
+ if (data.length !== 79 * 2) throw new Error("invalid data length")
108
+ return data
109
+ }
110
+
111
+ export function getBurnTransactionInfo(address: string, vin: Vin[] = [], vouts: Vout[] = []) {
112
+ let lockerVinIndex = vin.findIndex((vi) => vi.address === address)
113
+ if (lockerVinIndex >= 0) {
114
+ let lockerVin = { ...vin[lockerVinIndex], vinIndex: lockerVinIndex }
115
+ let totalInputValue = vin.reduce((acc, current) => acc + current.value, 0)
116
+ let receivers: (Vout & {
117
+ index: number
118
+ })[] = []
119
+ let changes: (Vout & {
120
+ index: number
121
+ })[] = []
122
+ vouts.forEach((vout, i) => {
123
+ let voutWithIndex = {
124
+ ...vout,
125
+ index: i,
126
+ }
127
+ if (voutWithIndex.address === address) {
128
+ changes.push(voutWithIndex)
129
+ } else {
130
+ receivers.push(voutWithIndex)
131
+ }
132
+ })
133
+ return { receivers, changes, totalInputValue, lockerVin }
134
+ }
135
+ return
136
+ }
137
+
138
+ //
139
+
140
+ function parseWrapRequest(data: string) {
141
+ let parsedData: any = {}
142
+ parsedData.requestType = "wrap"
143
+ let offset = 0
144
+ parsedData.chainId = BigNumber(`0x${data.slice(offset, (offset += 2))}`).toNumber() // 1 bytes
145
+ parsedData.appId = BigNumber(`0x${data.slice(offset, (offset += 4))}`).toNumber() // 2 bytes
146
+ parsedData.recipientAddress = `0x${data.slice(offset, (offset += 40))}` // 20 bytes
147
+ parsedData.percentageFee = BigNumber(`0x${data.slice(offset, (offset += 4))}`)
148
+ .dividedBy(100)
149
+ .toNumber() // 2 bytes
150
+ parsedData.speed = data.slice(offset, (offset += 2)) === "01" // 1 byte
151
+ if (data.length === offset) {
152
+ return {
153
+ status: true,
154
+ data: parsedData as WrapOpReturnWithType,
155
+ }
156
+ }
157
+
158
+ parsedData.requestType = "swapAndWrap"
159
+ parsedData.exchangeTokenAddress = `0x${data.slice(offset, (offset += 40))}` // 20 bytes
160
+ parsedData.outputAmount = new BigNumber(`0x${data.slice(offset, (offset += 56))}`).toFixed(0) // 28 bytes
161
+ parsedData.deadline = new BigNumber(`0x${data.slice(offset, (offset += 8))}`).toFixed(0) // 4 bytes
162
+ parsedData.isFixedToken = data.slice(offset, (offset += 2)) === "01" // 1 byte
163
+ if (data.length === offset) {
164
+ return {
165
+ status: true,
166
+ data: parsedData as WrapOpReturnWithType,
167
+ }
168
+ }
169
+
170
+ return {
171
+ status: false,
172
+ message: `invalid OP_RETURN data for requestType: 'transfer or exchange'. invalid data length : ${data.length} - valid length : ${offset}`,
173
+ code: "INVALID_OP_RETURN",
174
+ }
175
+ }
176
+
177
+ // function parseLendAndBorrowRequest(data: string) {
178
+ // let parsedData: any = {}
179
+ // parsedData.requestType = "lend"
180
+
181
+ // let offset = 0
182
+ // parsedData.chainId = BigNumber(`0x${data.slice(offset, (offset += 2))}`) // 1 bytes
183
+ // parsedData.appId = BigNumber(`0x${data.slice(offset, (offset += 4))}`) // 2 bytes
184
+ // parsedData.recipientAddress = `0x${data.slice(offset, (offset += 40))}` // 20 bytes
185
+ // parsedData.percentageFee = BigNumber(`0x${data.slice(offset, (offset += 4))}`) / 100 // 2 bytes
186
+ // parsedData.mode = BigNumber(`0x${data.slice(offset, (offset += 2))}`) // 1 byte
187
+ // if (data.length === offset) {
188
+ // return {
189
+ // status: true,
190
+ // data: parsedData,
191
+ // }
192
+ // }
193
+ // parsedData.requestType = "borrow"
194
+ // parsedData.tokenAddress = `0x${data.slice(offset, (offset += 40))}` // 20 bytes
195
+ // parsedData.borrowAmount = BigNumber(`0x${data.slice(offset, (offset += 56))}`) // 28 bytes
196
+ // if (data.length === offset) {
197
+ // return {
198
+ // status: true,
199
+ // data: parsedData,
200
+ // }
201
+ // }
202
+ // return {
203
+ // status: false,
204
+ // message: `invalid OP_RETURN data for requestType: 'lend or borrow'. invalid data length : ${data.length} - valid length : ${offset}`,
205
+ // code: "INVALID_OP_RETURN",
206
+ // }
207
+ // }
208
+
209
+ export function parseWrapRawRequest(opReturnData: string) {
210
+ let data = opReturnData.slice(2, 4) === "4c" ? opReturnData.slice(6) : opReturnData.slice(4)
211
+ let appIdHex = data.slice(2, 6) // 2 bytes
212
+ if (!appIdHex) {
213
+ return {
214
+ status: false,
215
+ message: `invalid OP_RETURN data : ${data}`,
216
+ code: "INVALID_APP_ID",
217
+ }
218
+ }
219
+
220
+ let appId = BigNumber(`0x${appIdHex}`).toNumber() // 2 bytes
221
+
222
+ // get type base on appId
223
+ let requestType = Object.keys(teleswap.requestAppId).find(
224
+ (key) =>
225
+ appId >= teleswap.requestAppId[key as keyof typeof teleswap.requestAppId].appIdRange[0] &&
226
+ appId <= teleswap.requestAppId[key as keyof typeof teleswap.requestAppId].appIdRange[1],
227
+ )
228
+
229
+ switch (requestType) {
230
+ case "wrap":
231
+ case "wrapAndSwap":
232
+ return parseWrapRequest(data)
233
+ default:
234
+ return {
235
+ status: false,
236
+ message: `invalid appId : ${appId}`,
237
+ code: "INVALID_OP_RETURN",
238
+ }
239
+ }
240
+ }
241
+
242
+ export function checkAndParseWrapRequest(
243
+ vouts: Vout[],
244
+ lockerAddress: string,
245
+ { minTeleporterFeeAmount = 0 } = {},
246
+ ) {
247
+ let requestOutputIndex = vouts.findIndex((vout_) => vout_.script.startsWith("6a"))
248
+ if (requestOutputIndex >= 0) {
249
+ let opReturnData = vouts[requestOutputIndex]?.script || null
250
+ if (!opReturnData) {
251
+ return {
252
+ status: false,
253
+ message: "no data to validate. it should not happen",
254
+ code: "INVALID_OP_RETURN",
255
+ }
256
+ }
257
+
258
+ let valueOutputIndex = vouts.findIndex((vout_) => vout_.address === lockerAddress)
259
+ let value = valueOutputIndex >= 0 ? vouts[valueOutputIndex].value || 0 : 0
260
+
261
+ let response = parseWrapRawRequest(opReturnData)
262
+
263
+ if (!response.status || !("data" in response) || !response.data) {
264
+ return response as {
265
+ status: boolean
266
+ message: string
267
+ code: string
268
+ }
269
+ }
270
+
271
+ const data = response.data
272
+
273
+ if (+data.percentageFee > 100) {
274
+ return {
275
+ status: false,
276
+ message: `percentageFee greater than 100 is invalid. percentageFee: ${data.percentageFee}`,
277
+ code: "INVALID_FEE",
278
+ }
279
+ }
280
+
281
+ if ((data.percentageFee / 100) * +value <= minTeleporterFeeAmount) {
282
+ return {
283
+ status: false,
284
+ message: `fee amount is less than or equal minimum teleporter fee amount.percentageFee: ${
285
+ data.percentageFee
286
+ } - value: ${value} - feeAmount ${((data.percentageFee / 100) * +value).toFixed(
287
+ 8,
288
+ )} - minimumFee: ${minTeleporterFeeAmount}`,
289
+ code: "NOT_ACCEPTED_BY_TELEPORTER",
290
+ }
291
+ }
292
+
293
+ return { status: response.status, data, value, valueOutputIndex }
294
+ }
295
+ return {
296
+ status: false,
297
+ message: "transaction outputs must include an OP_RETURN",
298
+ code: "NO_OP_RETURN",
299
+ }
300
+ }