@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.
- package/.tmp/psbt/sign-transaction.ts +9 -3
- package/dist/bitcoin-interface-ordinal.d.ts +104 -0
- package/dist/bitcoin-interface-ordinal.d.ts.map +1 -0
- package/dist/bitcoin-interface-ordinal.js +113 -0
- package/dist/bitcoin-interface-ordinal.js.map +1 -0
- package/dist/bitcoin-interface-teleswap.d.ts +148 -0
- package/dist/bitcoin-interface-teleswap.d.ts.map +1 -0
- package/dist/bitcoin-interface-teleswap.js +179 -0
- package/dist/bitcoin-interface-teleswap.js.map +1 -0
- package/dist/bitcoin-interface.d.ts +45 -333
- package/dist/bitcoin-interface.d.ts.map +1 -1
- package/dist/bitcoin-interface.js +68 -202
- package/dist/bitcoin-interface.js.map +1 -1
- package/dist/bitcoin-utils.d.ts +6 -53
- package/dist/bitcoin-utils.d.ts.map +1 -1
- package/dist/bitcoin-utils.js +4 -4
- package/dist/bitcoin-utils.js.map +1 -1
- package/dist/{bitcoin-base.d.ts → bitcoin-wallet-base.d.ts} +8 -8
- package/dist/bitcoin-wallet-base.d.ts.map +1 -0
- package/dist/{bitcoin-base.js → bitcoin-wallet-base.js} +12 -11
- package/dist/bitcoin-wallet-base.js.map +1 -0
- package/dist/helper/brc20-helper.d.ts +43 -0
- package/dist/helper/brc20-helper.d.ts.map +1 -0
- package/dist/helper/brc20-helper.js +129 -0
- package/dist/helper/brc20-helper.js.map +1 -0
- package/dist/helper/index.d.ts +4 -0
- package/dist/helper/index.d.ts.map +1 -0
- package/dist/helper/index.js +30 -0
- package/dist/helper/index.js.map +1 -0
- package/dist/helper/ordinal-helper.d.ts +13 -0
- package/dist/helper/ordinal-helper.d.ts.map +1 -0
- package/dist/helper/ordinal-helper.js +127 -0
- package/dist/helper/ordinal-helper.js.map +1 -0
- package/dist/helper/teleswap-helper.d.ts +84 -0
- package/dist/helper/teleswap-helper.d.ts.map +1 -0
- package/dist/helper/teleswap-helper.js +181 -0
- package/dist/helper/teleswap-helper.js.map +1 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -3
- package/dist/index.js.map +1 -1
- package/dist/ordinal-wallet.d.ts +536 -0
- package/dist/ordinal-wallet.d.ts.map +1 -0
- package/dist/ordinal-wallet.js +446 -0
- package/dist/ordinal-wallet.js.map +1 -0
- package/dist/sign/index.d.ts +2 -0
- package/dist/sign/index.d.ts.map +1 -0
- package/dist/sign/index.js +9 -0
- package/dist/sign/index.js.map +1 -0
- package/dist/sign/sign-transaction.d.ts +2 -1
- package/dist/sign/sign-transaction.d.ts.map +1 -1
- package/dist/sign/sign-transaction.js +14 -33
- package/dist/sign/sign-transaction.js.map +1 -1
- package/dist/teleswap-wallet.d.ts +54 -0
- package/dist/teleswap-wallet.d.ts.map +1 -0
- package/dist/teleswap-wallet.js +87 -0
- package/dist/teleswap-wallet.js.map +1 -0
- package/dist/transaction-builder/bitcoin-transaction-builder.d.ts +3 -21
- package/dist/transaction-builder/bitcoin-transaction-builder.d.ts.map +1 -1
- package/dist/transaction-builder/bitcoin-transaction-builder.js +7 -9
- package/dist/transaction-builder/bitcoin-transaction-builder.js.map +1 -1
- package/dist/transaction-builder/index.d.ts +4 -0
- package/dist/transaction-builder/index.d.ts.map +1 -0
- package/dist/transaction-builder/index.js +20 -0
- package/dist/transaction-builder/index.js.map +1 -0
- package/dist/transaction-builder/ordinal-transaction-builder.d.ts +63 -0
- package/dist/transaction-builder/ordinal-transaction-builder.d.ts.map +1 -0
- package/dist/transaction-builder/ordinal-transaction-builder.js +131 -0
- package/dist/transaction-builder/ordinal-transaction-builder.js.map +1 -0
- package/dist/transaction-builder/transaction-builder.d.ts +32 -5
- package/dist/transaction-builder/transaction-builder.d.ts.map +1 -1
- package/dist/transaction-builder/transaction-builder.js +65 -49
- package/dist/transaction-builder/transaction-builder.js.map +1 -1
- package/dist/type.d.ts +43 -0
- package/dist/type.d.ts.map +1 -0
- package/dist/type.js +3 -0
- package/dist/type.js.map +1 -0
- package/dist/utils/tools.d.ts +4 -4
- package/dist/utils/tools.d.ts.map +1 -1
- package/dist/utils/tools.js +8 -5
- package/dist/utils/tools.js.map +1 -1
- package/package.json +6 -8
- package/src/bitcoin-interface-ordinal.ts +128 -0
- package/src/bitcoin-interface-teleswap.ts +255 -0
- package/src/bitcoin-interface.ts +99 -303
- package/src/bitcoin-utils.ts +6 -32
- package/src/{bitcoin-base.ts → bitcoin-wallet-base.ts} +20 -14
- package/src/helper/brc20-helper.ts +181 -0
- package/src/helper/index.ts +3 -0
- package/src/helper/ordinal-helper.ts +118 -0
- package/src/helper/teleswap-helper.ts +300 -0
- package/src/index.ts +13 -3
- package/src/ordinal-wallet.ts +738 -0
- package/src/sign/index.ts +1 -0
- package/src/sign/sign-transaction.ts +20 -9
- package/src/teleswap-wallet.ts +155 -0
- package/src/transaction-builder/bitcoin-transaction-builder.ts +8 -25
- package/src/transaction-builder/index.ts +3 -0
- package/src/transaction-builder/ordinal-transaction-builder.ts +147 -0
- package/src/transaction-builder/transaction-builder.ts +117 -60
- package/src/type.ts +43 -0
- package/src/utils/tools.ts +17 -11
- package/tsconfig.json +1 -2
- package/dist/bitcoin-base.d.ts.map +0 -1
- package/dist/bitcoin-base.js.map +0 -1
- package/dist/bitcoin-utils-2.d.ts +0 -2
- package/dist/bitcoin-utils-2.d.ts.map +0 -1
- package/dist/bitcoin-utils-2.js +0 -13
- package/dist/bitcoin-utils-2.js.map +0 -1
- package/dist/bundle.js +0 -17
- package/dist/helper/burn-request-helper.d.ts +0 -7
- package/dist/helper/burn-request-helper.d.ts.map +0 -1
- package/dist/helper/burn-request-helper.js +0 -26
- package/dist/helper/burn-request-helper.js.map +0 -1
- package/dist/helper/teleport-request-helper.d.ts +0 -47
- package/dist/helper/teleport-request-helper.d.ts.map +0 -1
- package/dist/helper/teleport-request-helper.js +0 -146
- package/dist/helper/teleport-request-helper.js.map +0 -1
- package/dist/mempool-space.d.ts +0 -69
- package/dist/mempool-space.d.ts.map +0 -1
- package/dist/mempool-space.js +0 -266
- package/dist/mempool-space.js.map +0 -1
- package/dist/teleport-dao-payments.d.ts +0 -76
- package/dist/teleport-dao-payments.d.ts.map +0 -1
- package/dist/teleport-dao-payments.js +0 -217
- package/dist/teleport-dao-payments.js.map +0 -1
- package/src/helper/burn-request-helper.js +0 -27
- package/src/helper/teleport-request-helper.js +0 -181
- package/src/teleport-dao-payments.ts +0 -347
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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,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
|
+
}
|