@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as BaseBitcoinSigner } from "./sign-transaction"
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Psbt, crypto, Network } from "bitcoinjs-lib"
|
|
2
2
|
// import BIP32Factory from "bip32"
|
|
3
3
|
import ecc from "@bitcoinerlab/secp256k1"
|
|
4
|
-
import
|
|
5
|
-
|
|
4
|
+
import ECPairFactory from "ecpair"
|
|
5
|
+
|
|
6
|
+
const ECPair = ECPairFactory(ecc)
|
|
6
7
|
|
|
7
8
|
function tapTweakHash(pubKey: Buffer, h?: Buffer) {
|
|
8
9
|
return crypto.taggedHash("TapTweak", Buffer.concat(h ? [pubKey, h] : [pubKey]))
|
|
@@ -10,14 +11,14 @@ function tapTweakHash(pubKey: Buffer, h?: Buffer) {
|
|
|
10
11
|
|
|
11
12
|
function tweakSigner(
|
|
12
13
|
privateKey: Buffer,
|
|
14
|
+
network: Network,
|
|
13
15
|
opts = {} as {
|
|
14
16
|
[key: string]: Buffer
|
|
15
17
|
},
|
|
16
|
-
network: Network,
|
|
17
18
|
) {
|
|
18
19
|
let newPrv = privateKey
|
|
19
20
|
let keyPair = ECPair.fromPrivateKey(privateKey, {
|
|
20
|
-
network
|
|
21
|
+
network,
|
|
21
22
|
compressed: true,
|
|
22
23
|
})
|
|
23
24
|
|
|
@@ -36,12 +37,13 @@ function tweakSigner(
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
return ECPair.fromPrivateKey(Buffer.from(tweakedPrivateKey), {
|
|
39
|
-
network
|
|
40
|
+
network,
|
|
40
41
|
})
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
class BitcoinLikeSignTransaction {
|
|
44
45
|
network: Network
|
|
46
|
+
|
|
45
47
|
constructor(network: Network) {
|
|
46
48
|
this.network = network
|
|
47
49
|
}
|
|
@@ -49,8 +51,11 @@ class BitcoinLikeSignTransaction {
|
|
|
49
51
|
async signPsbt(
|
|
50
52
|
unsignedPsbt: {
|
|
51
53
|
unsignedTransaction: string
|
|
54
|
+
inputsToSign?: number[]
|
|
52
55
|
},
|
|
53
56
|
privateKey: Buffer,
|
|
57
|
+
sighashTypes?: any[],
|
|
58
|
+
usingTweakSignerIfNeeded = true,
|
|
54
59
|
) {
|
|
55
60
|
const { network } = this
|
|
56
61
|
const keyPair = ECPair.fromPrivateKey(privateKey, {
|
|
@@ -64,12 +69,18 @@ class BitcoinLikeSignTransaction {
|
|
|
64
69
|
let numberOfInputs = psbt.inputCount
|
|
65
70
|
|
|
66
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
|
+
}
|
|
67
76
|
let type = psbt.getInputType(i)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
|
|
78
|
+
if (usingTweakSignerIfNeeded && type === "nonstandard") {
|
|
79
|
+
console.log("using tweak signer")
|
|
80
|
+
let a = tweakSigner(privateKey, this.network)
|
|
81
|
+
await psbt.signInputAsync(i, a, sighashTypes)
|
|
71
82
|
} else {
|
|
72
|
-
await psbt.signInputAsync(i, keyPair)
|
|
83
|
+
await psbt.signInputAsync(i, keyPair, sighashTypes)
|
|
73
84
|
}
|
|
74
85
|
}
|
|
75
86
|
|
|
@@ -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
|
-
import BaseTransactionBuilder from "./transaction-builder"
|
|
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
|
+
}
|
|
@@ -8,28 +8,48 @@ const coinselectSplit = require("coinselect/split")
|
|
|
8
8
|
const coinselectAccumulative = require("coinselect/accumulative")
|
|
9
9
|
|
|
10
10
|
// https://bitcoin.stackexchange.com/questions/84004/how-do-virtual-size-stripped-size-and-raw-size-compare-between-legacy-address-f
|
|
11
|
-
const componentBytes = {
|
|
11
|
+
// export const componentBytes = {
|
|
12
|
+
// bytePerInput: {
|
|
13
|
+
// p2pkh: 148,
|
|
14
|
+
// p2wpkh: 70, // 68
|
|
15
|
+
// "p2sh-p2wpkh": 91,
|
|
16
|
+
// p2tr: 60, // actual 58
|
|
17
|
+
// },
|
|
18
|
+
// baseTxBytes: 10 + 5, // +5 extra bytes to be sure
|
|
19
|
+
// bytePerOutput: {
|
|
20
|
+
// p2pkh: 35, // 34
|
|
21
|
+
// p2wpkh: 35, // 31
|
|
22
|
+
// p2sh: 35, // 32
|
|
23
|
+
// p2tr: 45, // 43
|
|
24
|
+
// default: 35,
|
|
25
|
+
// },
|
|
26
|
+
//
|
|
27
|
+
// }
|
|
28
|
+
export const componentBytes = {
|
|
12
29
|
bytePerInput: {
|
|
13
30
|
p2pkh: 148,
|
|
14
|
-
p2wpkh:
|
|
31
|
+
p2wpkh: 68, // 68
|
|
15
32
|
"p2sh-p2wpkh": 91,
|
|
16
|
-
p2tr:
|
|
33
|
+
p2tr: 58, // actual 58
|
|
34
|
+
default: 100,
|
|
17
35
|
},
|
|
18
|
-
baseTxBytes: 10 + 5, // +
|
|
36
|
+
baseTxBytes: 10 + 5, // +5 extra bytes to be sure
|
|
19
37
|
bytePerOutput: {
|
|
20
|
-
p2pkh:
|
|
21
|
-
p2wpkh:
|
|
22
|
-
p2sh:
|
|
23
|
-
p2tr:
|
|
38
|
+
p2pkh: 34, // 34
|
|
39
|
+
p2wpkh: 31, // 31
|
|
40
|
+
p2sh: 32, // 32
|
|
41
|
+
p2tr: 43, // 43
|
|
24
42
|
default: 35,
|
|
43
|
+
max: 45,
|
|
25
44
|
},
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
45
|
+
|
|
46
|
+
scriptExtraBytes: {
|
|
47
|
+
lessThan255: 12,
|
|
48
|
+
moreThan255: 15,
|
|
29
49
|
},
|
|
30
50
|
}
|
|
31
51
|
|
|
32
|
-
const DUST = 1000
|
|
52
|
+
export const DUST = 1000
|
|
33
53
|
|
|
34
54
|
export type Utxo = {
|
|
35
55
|
hash: string
|
|
@@ -97,7 +117,23 @@ export type ExtendedUnsignedTransaction = {
|
|
|
97
117
|
change: TargetAddress | undefined
|
|
98
118
|
}
|
|
99
119
|
|
|
100
|
-
|
|
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
|
+
|
|
136
|
+
export class BaseTransactionBuilder {
|
|
101
137
|
testnet: boolean
|
|
102
138
|
network: bitcoin.Network
|
|
103
139
|
maximumNumberOfOutputsInTransaction: number
|
|
@@ -177,6 +213,52 @@ class BaseBitcoinLikeTransaction {
|
|
|
177
213
|
return extendedUtxo
|
|
178
214
|
}
|
|
179
215
|
|
|
216
|
+
calculateTxSize(
|
|
217
|
+
inputTypes: string[],
|
|
218
|
+
outputs: {
|
|
219
|
+
script?: Buffer
|
|
220
|
+
address?: string
|
|
221
|
+
value: number
|
|
222
|
+
}[],
|
|
223
|
+
changeAddressType = "default",
|
|
224
|
+
) {
|
|
225
|
+
const inputsSizes = inputTypes.map(
|
|
226
|
+
(addressType) =>
|
|
227
|
+
componentBytes.bytePerInput[addressType as keyof typeof componentBytes.bytePerInput],
|
|
228
|
+
)
|
|
229
|
+
const outputSizes = outputs.map((outP: any) => {
|
|
230
|
+
if (outP.address) {
|
|
231
|
+
let addressType = "default"
|
|
232
|
+
try {
|
|
233
|
+
addressType = getAddressType(outP.address, this.network)
|
|
234
|
+
} catch {
|
|
235
|
+
addressType = "default"
|
|
236
|
+
}
|
|
237
|
+
return componentBytes.bytePerOutput[
|
|
238
|
+
addressType as keyof typeof componentBytes.bytePerOutput
|
|
239
|
+
]
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (outP.script) {
|
|
243
|
+
if (outP.script.byteLength < 255) {
|
|
244
|
+
return outP.script.byteLength + componentBytes.scriptExtraBytes.lessThan255
|
|
245
|
+
}
|
|
246
|
+
return outP.script.byteLength + componentBytes.scriptExtraBytes.moreThan255
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return componentBytes.bytePerOutput[
|
|
250
|
+
changeAddressType as keyof typeof componentBytes.bytePerOutput
|
|
251
|
+
]
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
const txSize =
|
|
255
|
+
componentBytes.baseTxBytes +
|
|
256
|
+
inputsSizes.reduce((a, c) => a + c, 0) +
|
|
257
|
+
outputSizes.reduce((a, c) => a + c, 0)
|
|
258
|
+
|
|
259
|
+
return txSize
|
|
260
|
+
}
|
|
261
|
+
|
|
180
262
|
helperHandleInputsAndOutputs({
|
|
181
263
|
targets,
|
|
182
264
|
extendedUtxo,
|
|
@@ -194,7 +276,7 @@ class BaseBitcoinLikeTransaction {
|
|
|
194
276
|
derivationPath?: string
|
|
195
277
|
masterFingerprint?: string
|
|
196
278
|
}
|
|
197
|
-
selectType?: "normal" | "accumulative" | "full"
|
|
279
|
+
selectType?: "normal" | "accumulative" | "full" | "inOrder"
|
|
198
280
|
}) {
|
|
199
281
|
const filteredUtxo = extendedUtxo.filter(
|
|
200
282
|
(u) =>
|
|
@@ -212,6 +294,9 @@ class BaseBitcoinLikeTransaction {
|
|
|
212
294
|
case "accumulative":
|
|
213
295
|
selectResponse = coinselectAccumulative(filteredUtxo, targets, Math.round(feeRate))
|
|
214
296
|
break
|
|
297
|
+
case "inOrder":
|
|
298
|
+
selectResponse = coinSelectInOrder(filteredUtxo, targets, Math.round(feeRate))
|
|
299
|
+
break
|
|
215
300
|
case "full":
|
|
216
301
|
if (!(targets[0] as TargetAddress).address) {
|
|
217
302
|
throw new Error()
|
|
@@ -222,7 +307,6 @@ class BaseBitcoinLikeTransaction {
|
|
|
222
307
|
Math.round(feeRate),
|
|
223
308
|
)
|
|
224
309
|
break
|
|
225
|
-
|
|
226
310
|
default:
|
|
227
311
|
break
|
|
228
312
|
}
|
|
@@ -247,49 +331,24 @@ class BaseBitcoinLikeTransaction {
|
|
|
247
331
|
fee = inputs.reduce((a, b) => a + b.value, 0) - outputs.reduce((a, b) => a + b.value, 0)
|
|
248
332
|
}
|
|
249
333
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const outputSizes = outputs.map((outP) => {
|
|
257
|
-
if (outP.address) {
|
|
258
|
-
let addressType = "default"
|
|
259
|
-
try {
|
|
260
|
-
addressType = getAddressType(outP.address, this.network)
|
|
261
|
-
} catch {
|
|
262
|
-
addressType = "default"
|
|
263
|
-
}
|
|
264
|
-
return componentBytes.bytePerOutput[
|
|
265
|
-
addressType as keyof typeof componentBytes.bytePerOutput
|
|
266
|
-
]
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (outP.script) {
|
|
270
|
-
if (outP.script.byteLength < 75) {
|
|
271
|
-
return componentBytes.opReturn.dataLessThan75
|
|
272
|
-
}
|
|
273
|
-
return componentBytes.opReturn.dataMoreThan75
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
let addressType = "default"
|
|
277
|
-
try {
|
|
278
|
-
addressType = getAddressType(changeObject?.address || "", this.network)
|
|
279
|
-
} catch {
|
|
280
|
-
addressType = "default"
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return componentBytes.bytePerOutput[addressType as keyof typeof componentBytes.bytePerOutput]
|
|
284
|
-
})
|
|
334
|
+
let changeAddressType = "default"
|
|
335
|
+
try {
|
|
336
|
+
changeAddressType = getAddressType(changeObject?.address || "", this.network)
|
|
337
|
+
} catch {
|
|
338
|
+
changeAddressType = "default"
|
|
339
|
+
}
|
|
285
340
|
|
|
286
341
|
const txSize =
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
342
|
+
this.calculateTxSize(
|
|
343
|
+
inputs.map((i) => i.signerInfo.addressType),
|
|
344
|
+
outputs,
|
|
345
|
+
changeAddressType,
|
|
346
|
+
) + componentBytes.bytePerOutput.default
|
|
347
|
+
|
|
348
|
+
let txFee = Math.round(txSize * feeRate)
|
|
349
|
+
if (Math.round(feeRate) === 1) {
|
|
350
|
+
txFee = Math.round(txFee + txFee * 0.1)
|
|
351
|
+
}
|
|
293
352
|
if (
|
|
294
353
|
inputs.reduce((a, b) => a + b.value, 0) -
|
|
295
354
|
outputs.filter((o) => o.address || o.script).reduce((a, b) => a + b.value, 0) -
|
|
@@ -363,7 +422,7 @@ class BaseBitcoinLikeTransaction {
|
|
|
363
422
|
derivationPath?: string
|
|
364
423
|
masterFingerprint?: string
|
|
365
424
|
}
|
|
366
|
-
selectType?: "normal" | "accumulative" | "full"
|
|
425
|
+
selectType?: "normal" | "accumulative" | "full" | "inOrder"
|
|
367
426
|
}) {
|
|
368
427
|
let {
|
|
369
428
|
inputs: filteredInputs,
|
|
@@ -617,7 +676,7 @@ class BaseBitcoinLikeTransaction {
|
|
|
617
676
|
|
|
618
677
|
changeAddress?: string | SignerInfo
|
|
619
678
|
fullAmount?: boolean
|
|
620
|
-
selectType?: "normal" | "accumulative" | "full"
|
|
679
|
+
selectType?: "normal" | "accumulative" | "full" | "inOrder"
|
|
621
680
|
}) {
|
|
622
681
|
if (!changeAddress && targets.length === 0) throw new Error("no target")
|
|
623
682
|
let changeObject =
|
|
@@ -644,5 +703,3 @@ class BaseBitcoinLikeTransaction {
|
|
|
644
703
|
return unsignedTransaction
|
|
645
704
|
}
|
|
646
705
|
}
|
|
647
|
-
|
|
648
|
-
export default BaseBitcoinLikeTransaction
|