@teleportdao/bitcoin 1.6.1 → 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/check.ts +101 -0
- package/.tmp/psbt/sign-transaction.ts +121 -0
- package/.tmp/rbf.ts +45 -0
- 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 +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -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/sign-transaction.d.ts +2 -4
- package/dist/sign/sign-transaction.d.ts.map +1 -1
- package/dist/sign/sign-transaction.js +26 -22
- 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 +2 -20
- package/dist/transaction-builder/bitcoin-transaction-builder.d.ts.map +1 -1
- package/dist/transaction-builder/bitcoin-transaction-builder.js +5 -4
- package/dist/transaction-builder/bitcoin-transaction-builder.js.map +1 -1
- package/dist/transaction-builder/index.d.ts +2 -1
- package/dist/transaction-builder/index.d.ts.map +1 -1
- package/dist/transaction-builder/index.js +2 -6
- package/dist/transaction-builder/index.js.map +1 -1
- 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 +10 -7
- package/dist/transaction-builder/transaction-builder.d.ts.map +1 -1
- package/dist/transaction-builder/transaction-builder.js +18 -9
- 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 +11 -3
- package/src/ordinal-wallet.ts +738 -0
- package/src/sign/sign-transaction.ts +42 -33
- package/src/teleswap-wallet.ts +155 -0
- package/src/transaction-builder/bitcoin-transaction-builder.ts +7 -24
- package/src/transaction-builder/index.ts +2 -1
- package/src/transaction-builder/ordinal-transaction-builder.ts +147 -0
- package/src/transaction-builder/transaction-builder.ts +33 -15
- 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
|
@@ -9,50 +9,53 @@ function tapTweakHash(pubKey: Buffer, h?: Buffer) {
|
|
|
9
9
|
return crypto.taggedHash("TapTweak", Buffer.concat(h ? [pubKey, h] : [pubKey]))
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
function tweakSigner(
|
|
13
|
+
privateKey: Buffer,
|
|
14
|
+
network: Network,
|
|
15
|
+
opts = {} as {
|
|
16
|
+
[key: string]: Buffer
|
|
17
|
+
},
|
|
18
|
+
) {
|
|
19
|
+
let newPrv = privateKey
|
|
20
|
+
let keyPair = ECPair.fromPrivateKey(privateKey, {
|
|
21
|
+
network,
|
|
22
|
+
compressed: true,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
if (!keyPair.privateKey) throw new Error("private key not exist")
|
|
26
|
+
|
|
27
|
+
if (keyPair.publicKey.toString("hex").startsWith("03")) {
|
|
28
|
+
newPrv = ecc.privateNegate(keyPair.privateKey) as Buffer
|
|
16
29
|
}
|
|
17
30
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
let newPrv = privateKey
|
|
26
|
-
let keyPair = ECPair.fromPrivateKey(privateKey, {
|
|
27
|
-
network: network,
|
|
28
|
-
compressed: true,
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
if (!keyPair.privateKey) throw new Error("private key not exist")
|
|
31
|
+
const tweakedPrivateKey = ecc.privateAdd(
|
|
32
|
+
newPrv,
|
|
33
|
+
tapTweakHash(Buffer.from(keyPair.publicKey.toString("hex").slice(2), "hex"), opts?.tweakHash),
|
|
34
|
+
)
|
|
35
|
+
if (!tweakedPrivateKey) {
|
|
36
|
+
throw new Error("Invalid tweaked private key!")
|
|
37
|
+
}
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
return ECPair.fromPrivateKey(Buffer.from(tweakedPrivateKey), {
|
|
40
|
+
network,
|
|
41
|
+
})
|
|
42
|
+
}
|
|
36
43
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
tapTweakHash(Buffer.from(keyPair.publicKey.toString("hex").slice(2), "hex"), opts?.tweakHash),
|
|
40
|
-
)
|
|
41
|
-
if (!tweakedPrivateKey) {
|
|
42
|
-
throw new Error("Invalid tweaked private key!")
|
|
43
|
-
}
|
|
44
|
+
class BitcoinLikeSignTransaction {
|
|
45
|
+
network: Network
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
})
|
|
47
|
+
constructor(network: Network) {
|
|
48
|
+
this.network = network
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
async signPsbt(
|
|
51
52
|
unsignedPsbt: {
|
|
52
53
|
unsignedTransaction: string
|
|
54
|
+
inputsToSign?: number[]
|
|
53
55
|
},
|
|
54
56
|
privateKey: Buffer,
|
|
55
57
|
sighashTypes?: any[],
|
|
58
|
+
usingTweakSignerIfNeeded = true,
|
|
56
59
|
) {
|
|
57
60
|
const { network } = this
|
|
58
61
|
const keyPair = ECPair.fromPrivateKey(privateKey, {
|
|
@@ -66,9 +69,15 @@ class BitcoinLikeSignTransaction {
|
|
|
66
69
|
let numberOfInputs = psbt.inputCount
|
|
67
70
|
|
|
68
71
|
for (let i = 0; i < numberOfInputs; i += 1) {
|
|
72
|
+
if (unsignedPsbt.inputsToSign && !unsignedPsbt.inputsToSign.includes(i)) {
|
|
73
|
+
// eslint-disable-next-line no-continue
|
|
74
|
+
continue
|
|
75
|
+
}
|
|
69
76
|
let type = psbt.getInputType(i)
|
|
70
|
-
|
|
71
|
-
|
|
77
|
+
|
|
78
|
+
if (usingTweakSignerIfNeeded && type === "nonstandard") {
|
|
79
|
+
console.log("using tweak signer")
|
|
80
|
+
let a = tweakSigner(privateKey, this.network)
|
|
72
81
|
await psbt.signInputAsync(i, a, sighashTypes)
|
|
73
82
|
} else {
|
|
74
83
|
await psbt.signInputAsync(i, keyPair, sighashTypes)
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ExtendedUtxo,
|
|
3
|
+
SignerInfo,
|
|
4
|
+
TargetAddress,
|
|
5
|
+
} from "./transaction-builder/transaction-builder"
|
|
6
|
+
import { BitcoinBaseWallet } from "./bitcoin-wallet-base"
|
|
7
|
+
import { generateWrapOpReturn } from "./helper/teleswap-helper"
|
|
8
|
+
|
|
9
|
+
export type TransferRequest = {
|
|
10
|
+
changeAddress?: string
|
|
11
|
+
lockerAddress: string
|
|
12
|
+
amount: number
|
|
13
|
+
fullAmount?: boolean
|
|
14
|
+
//-----------
|
|
15
|
+
chainId: number
|
|
16
|
+
appId: number
|
|
17
|
+
recipientAddress: string // 20 bytes
|
|
18
|
+
percentageFee: number // 2 bytes in satoshi
|
|
19
|
+
speed?: number | boolean // 1 byte
|
|
20
|
+
isExchange?: boolean
|
|
21
|
+
exchangeTokenAddress?: string // 20 bytes
|
|
22
|
+
outputAmount?: number // 28 bytes
|
|
23
|
+
deadline?: number // 4 bytes
|
|
24
|
+
isFixedToken?: boolean // 1 byte
|
|
25
|
+
feeSpeed?: "normal" | "fast" | "slow"
|
|
26
|
+
staticFeeRate?: number
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class TeleswapWallet extends BitcoinBaseWallet {
|
|
30
|
+
// payment
|
|
31
|
+
async sendToMultipleAddress(
|
|
32
|
+
receivers: TargetAddress[],
|
|
33
|
+
feeSpeed: "normal" | "fast" | "slow" = "normal",
|
|
34
|
+
) {
|
|
35
|
+
if (!this.signerInfo || !this.privateKey) {
|
|
36
|
+
throw new Error("account not initialized")
|
|
37
|
+
}
|
|
38
|
+
let extendedUtxo = await this.getExtendedUtxo(this.signerInfo)
|
|
39
|
+
let feeRate = await this.transactionBuilder._getFeeRate(feeSpeed)
|
|
40
|
+
let unsignedTx = await this.transactionBuilder.processUnsignedTransaction({
|
|
41
|
+
extendedUtxo,
|
|
42
|
+
targets: receivers,
|
|
43
|
+
changeAddress: this.currentAccount,
|
|
44
|
+
feeRate,
|
|
45
|
+
fullAmount: false,
|
|
46
|
+
})
|
|
47
|
+
let signedPsbt = await this.signer.signPsbt(unsignedTx, this.privateKey)
|
|
48
|
+
let signedTx = this.signer.finalizePsbts([signedPsbt])
|
|
49
|
+
let txId = await this.transactionBuilder.sendTx(signedTx)
|
|
50
|
+
return txId
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// send
|
|
54
|
+
|
|
55
|
+
async wrapUnsigned(
|
|
56
|
+
recipientAddress: string,
|
|
57
|
+
amount: number,
|
|
58
|
+
percentageFee: number,
|
|
59
|
+
signer: SignerInfo,
|
|
60
|
+
lockerAddress: string,
|
|
61
|
+
exchange?: {
|
|
62
|
+
outputToken: string
|
|
63
|
+
outputAmount: string
|
|
64
|
+
deadline: string
|
|
65
|
+
isFixedToken?: boolean
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
chainId = 137, // 80001
|
|
69
|
+
appId = exchange ? 1 : 0,
|
|
70
|
+
} = {},
|
|
71
|
+
fullAmount = false,
|
|
72
|
+
staticExtendedUtxo?: ExtendedUtxo[],
|
|
73
|
+
staticFeeRate?: number,
|
|
74
|
+
changeAddress?: string,
|
|
75
|
+
) {
|
|
76
|
+
const dataHex = generateWrapOpReturn({
|
|
77
|
+
chainId,
|
|
78
|
+
appId,
|
|
79
|
+
recipientAddress,
|
|
80
|
+
percentageFee,
|
|
81
|
+
speed: false,
|
|
82
|
+
isExchange: !!exchange,
|
|
83
|
+
outputAmount: exchange?.outputAmount,
|
|
84
|
+
outputToken: exchange?.outputToken,
|
|
85
|
+
deadline: exchange?.deadline,
|
|
86
|
+
isFixedToken: exchange?.isFixedToken || false,
|
|
87
|
+
})
|
|
88
|
+
let extendedUtxo = staticExtendedUtxo || (await this.getExtendedUtxo(signer))
|
|
89
|
+
let feeRate = staticFeeRate || (await this.transactionBuilder._getFeeRate("normal"))
|
|
90
|
+
|
|
91
|
+
const targets = fullAmount
|
|
92
|
+
? [this.transactionBuilder.getOpReturnTarget(dataHex)]
|
|
93
|
+
: [
|
|
94
|
+
{
|
|
95
|
+
address: lockerAddress,
|
|
96
|
+
value: amount,
|
|
97
|
+
},
|
|
98
|
+
this.transactionBuilder.getOpReturnTarget(dataHex),
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
const unsignedTx = this.transactionBuilder.processUnsignedTransaction({
|
|
102
|
+
extendedUtxo,
|
|
103
|
+
feeRate,
|
|
104
|
+
targets,
|
|
105
|
+
changeAddress: changeAddress || signer.address,
|
|
106
|
+
fullAmount,
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
return unsignedTx
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async wrap(
|
|
113
|
+
recipientAddress: string,
|
|
114
|
+
amount: number,
|
|
115
|
+
percentageFee: number,
|
|
116
|
+
lockerAddress: string,
|
|
117
|
+
exchange?: {
|
|
118
|
+
outputToken: string
|
|
119
|
+
outputAmount: string
|
|
120
|
+
deadline: string
|
|
121
|
+
isFixedToken?: boolean
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
chainId = 137, // 80001
|
|
125
|
+
appId = exchange ? 1 : 0,
|
|
126
|
+
} = {},
|
|
127
|
+
fullAmount = false,
|
|
128
|
+
staticExtendedUtxo?: ExtendedUtxo[],
|
|
129
|
+
staticFeeRate?: number,
|
|
130
|
+
changeAddress?: string,
|
|
131
|
+
) {
|
|
132
|
+
if (!this.signerInfo || !this.privateKey) {
|
|
133
|
+
throw new Error("account not initialized")
|
|
134
|
+
}
|
|
135
|
+
let unsignedTransaction = await this.wrapUnsigned(
|
|
136
|
+
recipientAddress,
|
|
137
|
+
amount,
|
|
138
|
+
percentageFee,
|
|
139
|
+
this.signerInfo,
|
|
140
|
+
lockerAddress,
|
|
141
|
+
exchange,
|
|
142
|
+
{
|
|
143
|
+
chainId,
|
|
144
|
+
appId,
|
|
145
|
+
},
|
|
146
|
+
fullAmount,
|
|
147
|
+
staticExtendedUtxo,
|
|
148
|
+
staticFeeRate,
|
|
149
|
+
changeAddress,
|
|
150
|
+
)
|
|
151
|
+
let signedPsbt = await this.signer.signPsbt(unsignedTransaction, this.privateKey)
|
|
152
|
+
return this.sendSignedPsbt(signedPsbt)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
export default TeleswapWallet
|
|
@@ -1,26 +1,9 @@
|
|
|
1
1
|
import { BaseTransactionBuilder } from "./transaction-builder"
|
|
2
2
|
import { BitcoinInterface } from "../bitcoin-interface"
|
|
3
3
|
import * as bitcoin from "bitcoinjs-lib"
|
|
4
|
+
import { BitcoinConnectionInfo } from "../type"
|
|
4
5
|
|
|
5
|
-
export
|
|
6
|
-
rpc?: {
|
|
7
|
-
enabled?: boolean
|
|
8
|
-
url: string
|
|
9
|
-
headers?: {
|
|
10
|
-
[key: string]: string
|
|
11
|
-
}
|
|
12
|
-
auth?: {
|
|
13
|
-
username: string
|
|
14
|
-
password: string
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
api: {
|
|
18
|
-
enabled?: boolean
|
|
19
|
-
token?: string
|
|
20
|
-
provider: string
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
class BitcoinTransactionBuilder extends BaseTransactionBuilder {
|
|
6
|
+
export class BitcoinTransactionBuilder extends BaseTransactionBuilder {
|
|
24
7
|
btcInterface: BitcoinInterface
|
|
25
8
|
constructor(
|
|
26
9
|
connectionInfo: BitcoinConnectionInfo,
|
|
@@ -36,7 +19,7 @@ class BitcoinTransactionBuilder extends BaseTransactionBuilder {
|
|
|
36
19
|
}
|
|
37
20
|
|
|
38
21
|
async _getUtxo(userAddress: string) {
|
|
39
|
-
let utxos = await this.btcInterface.
|
|
22
|
+
let utxos = await this.btcInterface.getUtxo(userAddress)
|
|
40
23
|
return utxos.map((tx) => ({
|
|
41
24
|
hash: tx.txId as string,
|
|
42
25
|
value: +tx.value,
|
|
@@ -49,13 +32,13 @@ class BitcoinTransactionBuilder extends BaseTransactionBuilder {
|
|
|
49
32
|
}
|
|
50
33
|
|
|
51
34
|
async _getTransactionHex(transactionId: string): Promise<string> {
|
|
52
|
-
return this.btcInterface.
|
|
35
|
+
return this.btcInterface.getRawTransaction(transactionId)
|
|
53
36
|
}
|
|
54
37
|
|
|
55
38
|
async sendTx(txHex: string): Promise<string> {
|
|
56
|
-
let txId = await
|
|
39
|
+
let txId = await (
|
|
40
|
+
this.btcInterface.rpcProvider || this.btcInterface.apiProvider
|
|
41
|
+
).sendRawTransaction(txHex)
|
|
57
42
|
return txId
|
|
58
43
|
}
|
|
59
44
|
}
|
|
60
|
-
|
|
61
|
-
export default BitcoinTransactionBuilder
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { ExtendedUtxo, Target, componentBytes } from "../"
|
|
2
|
+
import * as bitcoin from "bitcoinjs-lib"
|
|
3
|
+
import { LEAF_VERSION_TAPSCRIPT } from "bitcoinjs-lib/src/payments/bip341"
|
|
4
|
+
import { BitcoinTransactionBuilder } from "./bitcoin-transaction-builder"
|
|
5
|
+
import { getOrdinalScript, toXOnly } from "../helper/ordinal-helper"
|
|
6
|
+
|
|
7
|
+
export class OrdinalTransactionBuilder extends BitcoinTransactionBuilder {
|
|
8
|
+
async createNftPsbt({
|
|
9
|
+
extendedUtxo,
|
|
10
|
+
nftExtendedUtxo,
|
|
11
|
+
receiverAddress,
|
|
12
|
+
feeRate,
|
|
13
|
+
changeAddress,
|
|
14
|
+
otherTargets,
|
|
15
|
+
}: {
|
|
16
|
+
nftExtendedUtxo: ExtendedUtxo
|
|
17
|
+
extendedUtxo: ExtendedUtxo[]
|
|
18
|
+
feeRate: number
|
|
19
|
+
changeAddress: string
|
|
20
|
+
receiverAddress: string
|
|
21
|
+
otherTargets?: Target[]
|
|
22
|
+
}) {
|
|
23
|
+
let targets: Target[] = [
|
|
24
|
+
{
|
|
25
|
+
address: receiverAddress,
|
|
26
|
+
value: nftExtendedUtxo.value,
|
|
27
|
+
},
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
if (otherTargets) targets.push(...otherTargets)
|
|
31
|
+
|
|
32
|
+
if (!changeAddress && targets.length === 0) throw new Error("no target")
|
|
33
|
+
let changeObject = {
|
|
34
|
+
address: changeAddress,
|
|
35
|
+
}
|
|
36
|
+
let { inputs, outputs, change, fee } = await this.filterAndConvertTxDataToStandardFormat({
|
|
37
|
+
extendedUtxo: [nftExtendedUtxo, ...extendedUtxo],
|
|
38
|
+
targets,
|
|
39
|
+
changeObject,
|
|
40
|
+
feeRate,
|
|
41
|
+
selectType: "inOrder",
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
let unsignedTx = this.createUnsignedTransaction({
|
|
45
|
+
inputs,
|
|
46
|
+
outputs,
|
|
47
|
+
change,
|
|
48
|
+
fee,
|
|
49
|
+
feeRate,
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
return unsignedTx
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
createOrdinalAddress(
|
|
56
|
+
file: {
|
|
57
|
+
buffer: Buffer
|
|
58
|
+
type: string
|
|
59
|
+
},
|
|
60
|
+
publicKey: Buffer,
|
|
61
|
+
) {
|
|
62
|
+
const internalPublicKey = toXOnly(publicKey).toString("hex")
|
|
63
|
+
let leafScript = getOrdinalScript(file, internalPublicKey)
|
|
64
|
+
|
|
65
|
+
const scriptTree = {
|
|
66
|
+
output: leafScript,
|
|
67
|
+
}
|
|
68
|
+
const redeem = {
|
|
69
|
+
output: leafScript,
|
|
70
|
+
redeemVersion: LEAF_VERSION_TAPSCRIPT,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const p2trTapOrdinalScript = bitcoin.payments.p2tr({
|
|
74
|
+
internalPubkey: Buffer.from(internalPublicKey, "hex"),
|
|
75
|
+
scriptTree,
|
|
76
|
+
network: this.network,
|
|
77
|
+
redeem,
|
|
78
|
+
})
|
|
79
|
+
let ordinalAddress = p2trTapOrdinalScript.address!
|
|
80
|
+
const controlBlock = p2trTapOrdinalScript.witness![p2trTapOrdinalScript.witness!.length - 1]
|
|
81
|
+
return {
|
|
82
|
+
ordinalAddress,
|
|
83
|
+
ordinalScript: p2trTapOrdinalScript.output!,
|
|
84
|
+
redeem,
|
|
85
|
+
controlBlock,
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
createInscribeUnsignedTx(
|
|
90
|
+
ordinalSpendDetails: {
|
|
91
|
+
ordinalAddress: string
|
|
92
|
+
ordinalScript: Buffer
|
|
93
|
+
redeem: {
|
|
94
|
+
output: Buffer
|
|
95
|
+
redeemVersion: number
|
|
96
|
+
}
|
|
97
|
+
controlBlock: Buffer
|
|
98
|
+
},
|
|
99
|
+
inscribeDeposit: {
|
|
100
|
+
hash: string
|
|
101
|
+
index: number
|
|
102
|
+
value: number
|
|
103
|
+
},
|
|
104
|
+
receiverAddress: string,
|
|
105
|
+
ordinalAmount = 600,
|
|
106
|
+
) {
|
|
107
|
+
const psbt = new bitcoin.Psbt({ network: this.network })
|
|
108
|
+
.addInput({
|
|
109
|
+
...inscribeDeposit,
|
|
110
|
+
witnessUtxo: { value: inscribeDeposit.value, script: ordinalSpendDetails.ordinalScript },
|
|
111
|
+
// tapInternalKey: toXOnly(node.publicKey),
|
|
112
|
+
// tapMerkleRoot: p2trTapOrdinalScript.hash!,
|
|
113
|
+
tapLeafScript: [
|
|
114
|
+
{
|
|
115
|
+
leafVersion: ordinalSpendDetails.redeem.redeemVersion,
|
|
116
|
+
script: ordinalSpendDetails.redeem.output,
|
|
117
|
+
controlBlock: ordinalSpendDetails.controlBlock,
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
})
|
|
121
|
+
.addOutput({
|
|
122
|
+
value: ordinalAmount,
|
|
123
|
+
address: receiverAddress,
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
unsignedTransaction: psbt.toBase64(),
|
|
128
|
+
inputs: [inscribeDeposit],
|
|
129
|
+
outputs: [
|
|
130
|
+
{
|
|
131
|
+
value: ordinalAmount,
|
|
132
|
+
address: receiverAddress,
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
fee: inscribeDeposit.value - ordinalAmount,
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// use with caution. just segwit address
|
|
140
|
+
getUnsignedPsbtTxId(unsignedPsbt: string): string {
|
|
141
|
+
// use with caution
|
|
142
|
+
let psbt = bitcoin.Psbt.fromBase64(unsignedPsbt, {
|
|
143
|
+
network: this.network,
|
|
144
|
+
})
|
|
145
|
+
return (psbt as any).__CACHE.__TX.getId()
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -23,10 +23,7 @@ const coinselectAccumulative = require("coinselect/accumulative")
|
|
|
23
23
|
// p2tr: 45, // 43
|
|
24
24
|
// default: 35,
|
|
25
25
|
// },
|
|
26
|
-
//
|
|
27
|
-
// dataLessThan75: 31,
|
|
28
|
-
// dataMoreThan75: 91,
|
|
29
|
-
// },
|
|
26
|
+
//
|
|
30
27
|
// }
|
|
31
28
|
export const componentBytes = {
|
|
32
29
|
bytePerInput: {
|
|
@@ -34,6 +31,7 @@ export const componentBytes = {
|
|
|
34
31
|
p2wpkh: 68, // 68
|
|
35
32
|
"p2sh-p2wpkh": 91,
|
|
36
33
|
p2tr: 58, // actual 58
|
|
34
|
+
default: 100,
|
|
37
35
|
},
|
|
38
36
|
baseTxBytes: 10 + 5, // +5 extra bytes to be sure
|
|
39
37
|
bytePerOutput: {
|
|
@@ -42,14 +40,16 @@ export const componentBytes = {
|
|
|
42
40
|
p2sh: 32, // 32
|
|
43
41
|
p2tr: 43, // 43
|
|
44
42
|
default: 35,
|
|
43
|
+
max: 45,
|
|
45
44
|
},
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
|
|
46
|
+
scriptExtraBytes: {
|
|
47
|
+
lessThan255: 12,
|
|
48
|
+
moreThan255: 15,
|
|
49
49
|
},
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
const DUST = 1000
|
|
52
|
+
export const DUST = 1000
|
|
53
53
|
|
|
54
54
|
export type Utxo = {
|
|
55
55
|
hash: string
|
|
@@ -117,6 +117,22 @@ export type ExtendedUnsignedTransaction = {
|
|
|
117
117
|
change: TargetAddress | undefined
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
function coinSelectInOrder(
|
|
121
|
+
utxos: { hash: string; index: number; value: number }[],
|
|
122
|
+
outputs: {
|
|
123
|
+
address?: string
|
|
124
|
+
script?: Buffer
|
|
125
|
+
value: number
|
|
126
|
+
}[],
|
|
127
|
+
feeRate: number,
|
|
128
|
+
) {
|
|
129
|
+
let response = coinselectAccumulative(utxos, outputs, 1)
|
|
130
|
+
return {
|
|
131
|
+
...response,
|
|
132
|
+
fee: +(+response.fee * feeRate).toFixed(),
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
120
136
|
export class BaseTransactionBuilder {
|
|
121
137
|
testnet: boolean
|
|
122
138
|
network: bitcoin.Network
|
|
@@ -224,10 +240,10 @@ export class BaseTransactionBuilder {
|
|
|
224
240
|
}
|
|
225
241
|
|
|
226
242
|
if (outP.script) {
|
|
227
|
-
if (outP.script.byteLength <
|
|
228
|
-
return componentBytes.
|
|
243
|
+
if (outP.script.byteLength < 255) {
|
|
244
|
+
return outP.script.byteLength + componentBytes.scriptExtraBytes.lessThan255
|
|
229
245
|
}
|
|
230
|
-
return componentBytes.
|
|
246
|
+
return outP.script.byteLength + componentBytes.scriptExtraBytes.moreThan255
|
|
231
247
|
}
|
|
232
248
|
|
|
233
249
|
return componentBytes.bytePerOutput[
|
|
@@ -260,7 +276,7 @@ export class BaseTransactionBuilder {
|
|
|
260
276
|
derivationPath?: string
|
|
261
277
|
masterFingerprint?: string
|
|
262
278
|
}
|
|
263
|
-
selectType?: "normal" | "accumulative" | "full"
|
|
279
|
+
selectType?: "normal" | "accumulative" | "full" | "inOrder"
|
|
264
280
|
}) {
|
|
265
281
|
const filteredUtxo = extendedUtxo.filter(
|
|
266
282
|
(u) =>
|
|
@@ -278,6 +294,9 @@ export class BaseTransactionBuilder {
|
|
|
278
294
|
case "accumulative":
|
|
279
295
|
selectResponse = coinselectAccumulative(filteredUtxo, targets, Math.round(feeRate))
|
|
280
296
|
break
|
|
297
|
+
case "inOrder":
|
|
298
|
+
selectResponse = coinSelectInOrder(filteredUtxo, targets, Math.round(feeRate))
|
|
299
|
+
break
|
|
281
300
|
case "full":
|
|
282
301
|
if (!(targets[0] as TargetAddress).address) {
|
|
283
302
|
throw new Error()
|
|
@@ -288,7 +307,6 @@ export class BaseTransactionBuilder {
|
|
|
288
307
|
Math.round(feeRate),
|
|
289
308
|
)
|
|
290
309
|
break
|
|
291
|
-
|
|
292
310
|
default:
|
|
293
311
|
break
|
|
294
312
|
}
|
|
@@ -404,7 +422,7 @@ export class BaseTransactionBuilder {
|
|
|
404
422
|
derivationPath?: string
|
|
405
423
|
masterFingerprint?: string
|
|
406
424
|
}
|
|
407
|
-
selectType?: "normal" | "accumulative" | "full"
|
|
425
|
+
selectType?: "normal" | "accumulative" | "full" | "inOrder"
|
|
408
426
|
}) {
|
|
409
427
|
let {
|
|
410
428
|
inputs: filteredInputs,
|
|
@@ -658,7 +676,7 @@ export class BaseTransactionBuilder {
|
|
|
658
676
|
|
|
659
677
|
changeAddress?: string | SignerInfo
|
|
660
678
|
fullAmount?: boolean
|
|
661
|
-
selectType?: "normal" | "accumulative" | "full"
|
|
679
|
+
selectType?: "normal" | "accumulative" | "full" | "inOrder"
|
|
662
680
|
}) {
|
|
663
681
|
if (!changeAddress && targets.length === 0) throw new Error("no target")
|
|
664
682
|
let changeObject =
|
package/src/type.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type Transaction = {
|
|
2
|
+
txId: string
|
|
3
|
+
version: number
|
|
4
|
+
locktime: number
|
|
5
|
+
blockNumber: number
|
|
6
|
+
blockHash: string
|
|
7
|
+
merkleProof?: {
|
|
8
|
+
intermediateNodes: string
|
|
9
|
+
transactionIndex: number
|
|
10
|
+
}
|
|
11
|
+
vout: {
|
|
12
|
+
address?: string
|
|
13
|
+
script: string
|
|
14
|
+
value: number
|
|
15
|
+
}[]
|
|
16
|
+
vin: {
|
|
17
|
+
txId: string
|
|
18
|
+
index: number
|
|
19
|
+
address?: string
|
|
20
|
+
script?: string
|
|
21
|
+
value?: number
|
|
22
|
+
}[]
|
|
23
|
+
address: string
|
|
24
|
+
addressScript: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type BitcoinConnectionInfo = {
|
|
28
|
+
rpc?: {
|
|
29
|
+
enabled?: boolean
|
|
30
|
+
url: string
|
|
31
|
+
headers?: {
|
|
32
|
+
[key: string]: string
|
|
33
|
+
}
|
|
34
|
+
auth?: {
|
|
35
|
+
username: string
|
|
36
|
+
password: string
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
api: {
|
|
40
|
+
token?: string
|
|
41
|
+
provider: string
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/utils/tools.ts
CHANGED
|
@@ -4,27 +4,33 @@ function sleep(ms: number) {
|
|
|
4
4
|
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
async function runWithRetries(
|
|
8
|
-
action: () =>
|
|
9
|
-
config
|
|
7
|
+
async function runWithRetries<T>(
|
|
8
|
+
action: () => Promise<T>,
|
|
9
|
+
config: {
|
|
10
|
+
maxTries?: number
|
|
11
|
+
retrySleep?: number
|
|
12
|
+
} = {
|
|
10
13
|
maxTries: 2,
|
|
11
14
|
retrySleep: 1000,
|
|
12
15
|
},
|
|
13
|
-
) {
|
|
14
|
-
const maxTries = config.maxTries
|
|
15
|
-
const retrySleep = config.retrySleep
|
|
16
|
-
|
|
16
|
+
): Promise<T> {
|
|
17
|
+
const maxTries = config.maxTries ?? 2
|
|
18
|
+
const retrySleep = config.retrySleep ?? 1000
|
|
19
|
+
|
|
20
|
+
let lastError: any
|
|
17
21
|
for (let count = 0; count < maxTries; count += 1) {
|
|
18
22
|
try {
|
|
19
23
|
return await action()
|
|
20
24
|
} catch (error: any) {
|
|
21
|
-
console.log(
|
|
22
|
-
|
|
25
|
+
console.log(`Attempt ${count + 1} failed: ${error.message}`)
|
|
23
26
|
lastError = error
|
|
27
|
+
if (count < maxTries - 1) {
|
|
28
|
+
await sleep(retrySleep)
|
|
29
|
+
}
|
|
24
30
|
}
|
|
25
|
-
await sleep(retrySleep)
|
|
26
31
|
}
|
|
27
|
-
|
|
32
|
+
|
|
33
|
+
throw lastError || new Error(`Function failed after ${maxTries} retries: ${lastError?.message}`)
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
function getRandomInteger(min: number, max: number) {
|
package/tsconfig.json
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
"extends": "../../tsconfig.json",
|
|
3
3
|
"compilerOptions": {
|
|
4
4
|
"rootDir": "src" /* Specify the root folder within your source files. */,
|
|
5
|
-
"outDir": "dist" /* Specify an output folder for all emitted files.
|
|
6
|
-
"baseUrl": "./src" /* Specify the base directory to resolve non-relative module names. */
|
|
5
|
+
"outDir": "dist" /* Specify an output folder for all emitted files. */
|
|
7
6
|
},
|
|
8
7
|
"include": ["src"],
|
|
9
8
|
"exclude": ["node_modules", "dist"]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bitcoin-base.d.ts","sourceRoot":"","sources":["../src/bitcoin-base.ts"],"names":[],"mappings":"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"}
|