@btc-vision/wallet-sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +24 -0
- package/es/bundle.js +2 -0
- package/es/bundle.js.LICENSE.txt +48 -0
- package/lib/address/index.d.ts +37 -0
- package/lib/address/index.js +235 -0
- package/lib/bitcoin-core.d.ts +5 -0
- package/lib/bitcoin-core.js +37 -0
- package/lib/constants.d.ts +1 -0
- package/lib/constants.js +5 -0
- package/lib/error.d.ts +20 -0
- package/lib/error.js +29 -0
- package/lib/index.d.ts +14 -0
- package/lib/index.js +50 -0
- package/lib/keyring/hd-keyring.d.ts +50 -0
- package/lib/keyring/hd-keyring.js +238 -0
- package/lib/keyring/index.d.ts +4 -0
- package/lib/keyring/index.js +20 -0
- package/lib/keyring/interfaces/SimpleKeyringOptions.d.ts +53 -0
- package/lib/keyring/interfaces/SimpleKeyringOptions.js +85 -0
- package/lib/keyring/keystone-keyring.d.ts +82 -0
- package/lib/keyring/keystone-keyring.js +377 -0
- package/lib/keyring/simple-keyring.d.ts +11 -0
- package/lib/keyring/simple-keyring.js +66 -0
- package/lib/message/bip322-simple.d.ts +19 -0
- package/lib/message/bip322-simple.js +182 -0
- package/lib/message/deterministic-ecdsa.d.ts +2 -0
- package/lib/message/deterministic-ecdsa.js +83 -0
- package/lib/message/ecdsa.d.ts +3 -0
- package/lib/message/ecdsa.js +32 -0
- package/lib/message/index.d.ts +3 -0
- package/lib/message/index.js +19 -0
- package/lib/network/index.d.ts +14 -0
- package/lib/network/index.js +40 -0
- package/lib/runes/index.d.ts +1 -0
- package/lib/runes/index.js +17 -0
- package/lib/runes/rund_id.d.ts +11 -0
- package/lib/runes/rund_id.js +30 -0
- package/lib/runes/varint.d.ts +15 -0
- package/lib/runes/varint.js +45 -0
- package/lib/src/address/index.d.ts +37 -0
- package/lib/src/bitcoin-core.d.ts +5 -0
- package/lib/src/constants.d.ts +1 -0
- package/lib/src/error.d.ts +20 -0
- package/lib/src/index.d.ts +14 -0
- package/lib/src/keyring/hd-keyring.d.ts +50 -0
- package/lib/src/keyring/index.d.ts +4 -0
- package/lib/src/keyring/interfaces/SimpleKeyringOptions.d.ts +53 -0
- package/lib/src/keyring/keystone-keyring.d.ts +82 -0
- package/lib/src/keyring/simple-keyring.d.ts +11 -0
- package/lib/src/message/bip322-simple.d.ts +19 -0
- package/lib/src/message/deterministic-ecdsa.d.ts +2 -0
- package/lib/src/message/ecdsa.d.ts +3 -0
- package/lib/src/message/index.d.ts +3 -0
- package/lib/src/network/index.d.ts +14 -0
- package/lib/src/runes/index.d.ts +1 -0
- package/lib/src/runes/rund_id.d.ts +11 -0
- package/lib/src/runes/varint.d.ts +15 -0
- package/lib/src/transaction/index.d.ts +3 -0
- package/lib/src/transaction/inscription-utxo.d.ts +33 -0
- package/lib/src/transaction/transaction.d.ts +52 -0
- package/lib/src/transaction/utxo.d.ts +35 -0
- package/lib/src/tx-helpers/index.d.ts +8 -0
- package/lib/src/tx-helpers/send-atomicals-ft.d.ts +16 -0
- package/lib/src/tx-helpers/send-atomicals-nft.d.ts +14 -0
- package/lib/src/tx-helpers/send-btc.d.ts +28 -0
- package/lib/src/tx-helpers/send-inscription.d.ts +16 -0
- package/lib/src/tx-helpers/send-inscriptions.d.ts +14 -0
- package/lib/src/tx-helpers/send-runes.d.ts +19 -0
- package/lib/src/tx-helpers/split-inscription-utxo.d.ts +15 -0
- package/lib/src/types.d.ts +59 -0
- package/lib/src/utils.d.ts +23 -0
- package/lib/src/wallet/abstract-wallet.d.ts +6 -0
- package/lib/src/wallet/estimate-wallet.d.ts +23 -0
- package/lib/src/wallet/index.d.ts +3 -0
- package/lib/src/wallet/local-wallet.d.ts +23 -0
- package/lib/test/address/address.test.d.ts +1 -0
- package/lib/test/keyring/hd-keyring.test.d.ts +1 -0
- package/lib/test/keyring/keystone-keyring.test.d.ts +1 -0
- package/lib/test/keyring/simple-keyring.test.d.ts +1 -0
- package/lib/test/message/message.test.d.ts +1 -0
- package/lib/test/runes/varint.test.d.ts +1 -0
- package/lib/test/transaction/transaction.test.d.ts +1 -0
- package/lib/test/transaction/utxo.test.d.ts +1 -0
- package/lib/test/tx-helpers/send-atomicals-ft.test.d.ts +1 -0
- package/lib/test/tx-helpers/send-atomicals-nft.test.d.ts +1 -0
- package/lib/test/tx-helpers/send-btc.test.d.ts +1 -0
- package/lib/test/tx-helpers/send-inscription.test.d.ts +1 -0
- package/lib/test/tx-helpers/send-inscriptions.test.d.ts +1 -0
- package/lib/test/tx-helpers/send-runes.test.d.ts +1 -0
- package/lib/test/tx-helpers/split-inscription-utxo.test.d.ts +1 -0
- package/lib/test/tx-helpers/utils.d.ts +217 -0
- package/lib/test/utils.d.ts +4 -0
- package/lib/test/wallet/local-wallet.test.d.ts +1 -0
- package/lib/transaction/index.d.ts +3 -0
- package/lib/transaction/index.js +19 -0
- package/lib/transaction/inscription-utxo.d.ts +33 -0
- package/lib/transaction/inscription-utxo.js +120 -0
- package/lib/transaction/transaction.d.ts +52 -0
- package/lib/transaction/transaction.js +358 -0
- package/lib/transaction/utxo.d.ts +35 -0
- package/lib/transaction/utxo.js +107 -0
- package/lib/tx-helpers/index.d.ts +8 -0
- package/lib/tx-helpers/index.js +18 -0
- package/lib/tx-helpers/send-atomicals-ft.d.ts +16 -0
- package/lib/tx-helpers/send-atomicals-ft.js +66 -0
- package/lib/tx-helpers/send-atomicals-nft.d.ts +14 -0
- package/lib/tx-helpers/send-atomicals-nft.js +45 -0
- package/lib/tx-helpers/send-btc.d.ts +28 -0
- package/lib/tx-helpers/send-btc.js +78 -0
- package/lib/tx-helpers/send-inscription.d.ts +16 -0
- package/lib/tx-helpers/send-inscription.js +49 -0
- package/lib/tx-helpers/send-inscriptions.d.ts +14 -0
- package/lib/tx-helpers/send-inscriptions.js +45 -0
- package/lib/tx-helpers/send-runes.d.ts +19 -0
- package/lib/tx-helpers/send-runes.js +101 -0
- package/lib/tx-helpers/split-inscription-utxo.d.ts +15 -0
- package/lib/tx-helpers/split-inscription-utxo.js +58 -0
- package/lib/types.d.ts +59 -0
- package/lib/types.js +15 -0
- package/lib/utils.d.ts +23 -0
- package/lib/utils.js +71 -0
- package/lib/wallet/abstract-wallet.d.ts +6 -0
- package/lib/wallet/abstract-wallet.js +2 -0
- package/lib/wallet/estimate-wallet.d.ts +23 -0
- package/lib/wallet/estimate-wallet.js +174 -0
- package/lib/wallet/index.d.ts +3 -0
- package/lib/wallet/index.js +19 -0
- package/lib/wallet/local-wallet.d.ts +23 -0
- package/lib/wallet/local-wallet.js +183 -0
- package/package.json +97 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.Transaction = void 0;
|
|
13
|
+
const address_1 = require("../address");
|
|
14
|
+
const bitcoin_core_1 = require("../bitcoin-core");
|
|
15
|
+
const constants_1 = require("../constants");
|
|
16
|
+
const error_1 = require("../error");
|
|
17
|
+
const network_1 = require("../network");
|
|
18
|
+
const types_1 = require("../types");
|
|
19
|
+
const utils_1 = require("../utils");
|
|
20
|
+
const wallet_1 = require("../wallet");
|
|
21
|
+
const utxo_1 = require("./utxo");
|
|
22
|
+
/**
|
|
23
|
+
* Convert UnspentOutput to PSBT TxInput
|
|
24
|
+
*/
|
|
25
|
+
function utxoToInput(utxo, estimate) {
|
|
26
|
+
if (utxo.addressType === types_1.AddressType.P2TR || utxo.addressType === types_1.AddressType.M44_P2TR) {
|
|
27
|
+
const data = {
|
|
28
|
+
hash: utxo.txid,
|
|
29
|
+
index: utxo.vout,
|
|
30
|
+
witnessUtxo: {
|
|
31
|
+
value: utxo.satoshis,
|
|
32
|
+
script: Buffer.from(utxo.scriptPk, 'hex')
|
|
33
|
+
},
|
|
34
|
+
tapInternalKey: (0, utils_1.toXOnly)(Buffer.from(utxo.pubkey, 'hex'))
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
data,
|
|
38
|
+
utxo
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
else if (utxo.addressType === types_1.AddressType.P2WPKH || utxo.addressType === types_1.AddressType.M44_P2WPKH) {
|
|
42
|
+
const data = {
|
|
43
|
+
hash: utxo.txid,
|
|
44
|
+
index: utxo.vout,
|
|
45
|
+
witnessUtxo: {
|
|
46
|
+
value: utxo.satoshis,
|
|
47
|
+
script: Buffer.from(utxo.scriptPk, 'hex')
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
return {
|
|
51
|
+
data,
|
|
52
|
+
utxo
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
else if (utxo.addressType === types_1.AddressType.P2PKH) {
|
|
56
|
+
if (!utxo.rawtx || estimate) {
|
|
57
|
+
const data = {
|
|
58
|
+
hash: utxo.txid,
|
|
59
|
+
index: utxo.vout,
|
|
60
|
+
witnessUtxo: {
|
|
61
|
+
value: utxo.satoshis,
|
|
62
|
+
script: Buffer.from(utxo.scriptPk, 'hex')
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
return {
|
|
66
|
+
data,
|
|
67
|
+
utxo
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
const data = {
|
|
72
|
+
hash: utxo.txid,
|
|
73
|
+
index: utxo.vout,
|
|
74
|
+
nonWitnessUtxo: Buffer.from(utxo.rawtx, 'hex')
|
|
75
|
+
};
|
|
76
|
+
return {
|
|
77
|
+
data,
|
|
78
|
+
utxo
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (utxo.addressType === types_1.AddressType.P2SH_P2WPKH) {
|
|
83
|
+
const redeemData = bitcoin_core_1.bitcoin.payments.p2wpkh({
|
|
84
|
+
pubkey: Buffer.from(utxo.pubkey, 'hex')
|
|
85
|
+
});
|
|
86
|
+
const data = {
|
|
87
|
+
hash: utxo.txid,
|
|
88
|
+
index: utxo.vout,
|
|
89
|
+
witnessUtxo: {
|
|
90
|
+
value: utxo.satoshis,
|
|
91
|
+
script: Buffer.from(utxo.scriptPk, 'hex')
|
|
92
|
+
},
|
|
93
|
+
redeemScript: redeemData.output
|
|
94
|
+
};
|
|
95
|
+
return {
|
|
96
|
+
data,
|
|
97
|
+
utxo
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Transaction
|
|
103
|
+
*/
|
|
104
|
+
class Transaction {
|
|
105
|
+
constructor() {
|
|
106
|
+
this.utxos = [];
|
|
107
|
+
this.inputs = [];
|
|
108
|
+
this.outputs = [];
|
|
109
|
+
this.changeOutputIndex = -1;
|
|
110
|
+
this.enableRBF = true;
|
|
111
|
+
this._cacheNetworkFee = 0;
|
|
112
|
+
this._cacheBtcUtxos = [];
|
|
113
|
+
this._cacheToSignInputs = [];
|
|
114
|
+
}
|
|
115
|
+
setNetworkType(network) {
|
|
116
|
+
this.networkType = network;
|
|
117
|
+
}
|
|
118
|
+
setEnableRBF(enable) {
|
|
119
|
+
this.enableRBF = enable;
|
|
120
|
+
}
|
|
121
|
+
setFeeRate(feeRate) {
|
|
122
|
+
this.feeRate = feeRate;
|
|
123
|
+
}
|
|
124
|
+
setChangeAddress(address) {
|
|
125
|
+
this.changedAddress = address;
|
|
126
|
+
}
|
|
127
|
+
addInput(utxo) {
|
|
128
|
+
this.utxos.push(utxo);
|
|
129
|
+
this.inputs.push(utxoToInput(utxo));
|
|
130
|
+
}
|
|
131
|
+
removeLastInput() {
|
|
132
|
+
this.utxos = this.utxos.slice(0, -1);
|
|
133
|
+
this.inputs = this.inputs.slice(0, -1);
|
|
134
|
+
}
|
|
135
|
+
getTotalInput() {
|
|
136
|
+
return this.inputs.reduce((pre, cur) => pre + cur.utxo.satoshis, 0);
|
|
137
|
+
}
|
|
138
|
+
getTotalOutput() {
|
|
139
|
+
return this.outputs.reduce((pre, cur) => pre + cur.value, 0);
|
|
140
|
+
}
|
|
141
|
+
getUnspent() {
|
|
142
|
+
return this.getTotalInput() - this.getTotalOutput();
|
|
143
|
+
}
|
|
144
|
+
calNetworkFee() {
|
|
145
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
146
|
+
const psbt = yield this.createEstimatePsbt();
|
|
147
|
+
const txSize = psbt.extractTransaction(true).virtualSize();
|
|
148
|
+
const fee = Math.ceil(txSize * this.feeRate);
|
|
149
|
+
return fee;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
addOutput(address, value) {
|
|
153
|
+
this.outputs.push({
|
|
154
|
+
address,
|
|
155
|
+
value
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
addOpreturn(data) {
|
|
159
|
+
const embed = bitcoin_core_1.bitcoin.payments.embed({ data });
|
|
160
|
+
this.outputs.push({
|
|
161
|
+
script: embed.output,
|
|
162
|
+
value: 0
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
addScriptOutput(script, value) {
|
|
166
|
+
this.outputs.push({
|
|
167
|
+
script,
|
|
168
|
+
value
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
getOutput(index) {
|
|
172
|
+
return this.outputs[index];
|
|
173
|
+
}
|
|
174
|
+
addChangeOutput(value) {
|
|
175
|
+
this.outputs.push({
|
|
176
|
+
address: this.changedAddress,
|
|
177
|
+
value
|
|
178
|
+
});
|
|
179
|
+
this.changeOutputIndex = this.outputs.length - 1;
|
|
180
|
+
}
|
|
181
|
+
getChangeOutput() {
|
|
182
|
+
return this.outputs[this.changeOutputIndex];
|
|
183
|
+
}
|
|
184
|
+
getChangeAmount() {
|
|
185
|
+
const output = this.getChangeOutput();
|
|
186
|
+
return output ? output.value : 0;
|
|
187
|
+
}
|
|
188
|
+
removeChangeOutput() {
|
|
189
|
+
this.outputs.splice(this.changeOutputIndex, 1);
|
|
190
|
+
this.changeOutputIndex = -1;
|
|
191
|
+
}
|
|
192
|
+
removeRecentOutputs(count) {
|
|
193
|
+
this.outputs.splice(-count);
|
|
194
|
+
}
|
|
195
|
+
toPsbt() {
|
|
196
|
+
const network = (0, network_1.toPsbtNetwork)(this.networkType);
|
|
197
|
+
const psbt = new bitcoin_core_1.bitcoin.Psbt({ network });
|
|
198
|
+
this.inputs.forEach((v, index) => {
|
|
199
|
+
if (v.utxo.addressType === types_1.AddressType.P2PKH) {
|
|
200
|
+
if (v.data.witnessUtxo) {
|
|
201
|
+
//@ts-ignore
|
|
202
|
+
psbt.__CACHE.__UNSAFE_SIGN_NONSEGWIT = true;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
psbt.data.addInput(v.data);
|
|
206
|
+
if (this.enableRBF) {
|
|
207
|
+
psbt.setInputSequence(index, 0xfffffffd);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
this.outputs.forEach((v) => {
|
|
211
|
+
if (v.address) {
|
|
212
|
+
psbt.addOutput({
|
|
213
|
+
address: v.address,
|
|
214
|
+
value: v.value
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
else if (v.script) {
|
|
218
|
+
psbt.addOutput({
|
|
219
|
+
script: v.script,
|
|
220
|
+
value: v.value
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
return psbt;
|
|
225
|
+
}
|
|
226
|
+
clone() {
|
|
227
|
+
const tx = new Transaction();
|
|
228
|
+
tx.setNetworkType(this.networkType);
|
|
229
|
+
tx.setFeeRate(this.feeRate);
|
|
230
|
+
tx.setEnableRBF(this.enableRBF);
|
|
231
|
+
tx.setChangeAddress(this.changedAddress);
|
|
232
|
+
tx.utxos = this.utxos.map((v) => Object.assign({}, v));
|
|
233
|
+
tx.inputs = this.inputs.map((v) => v);
|
|
234
|
+
tx.outputs = this.outputs.map((v) => v);
|
|
235
|
+
return tx;
|
|
236
|
+
}
|
|
237
|
+
createEstimatePsbt() {
|
|
238
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
239
|
+
const estimateWallet = wallet_1.EstimateWallet.fromRandom(this.inputs[0].utxo.addressType, this.networkType);
|
|
240
|
+
const scriptPk = (0, address_1.addressToScriptPk)(estimateWallet.address, this.networkType).toString('hex');
|
|
241
|
+
const tx = this.clone();
|
|
242
|
+
tx.utxos.forEach((v) => {
|
|
243
|
+
v.pubkey = estimateWallet.pubkey;
|
|
244
|
+
v.scriptPk = scriptPk;
|
|
245
|
+
});
|
|
246
|
+
tx.inputs = [];
|
|
247
|
+
tx.utxos.forEach((v) => {
|
|
248
|
+
const input = utxoToInput(v, true);
|
|
249
|
+
tx.inputs.push(input);
|
|
250
|
+
});
|
|
251
|
+
const psbt = tx.toPsbt();
|
|
252
|
+
const toSignInputs = tx.inputs.map((v, index) => ({
|
|
253
|
+
index,
|
|
254
|
+
publicKey: estimateWallet.pubkey
|
|
255
|
+
}));
|
|
256
|
+
yield estimateWallet.signPsbt(psbt, {
|
|
257
|
+
autoFinalized: true,
|
|
258
|
+
toSignInputs: toSignInputs
|
|
259
|
+
});
|
|
260
|
+
return psbt;
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
selectBtcUtxos() {
|
|
264
|
+
const totalInput = this.getTotalInput();
|
|
265
|
+
const totalOutput = this.getTotalOutput() + this._cacheNetworkFee;
|
|
266
|
+
if (totalInput < totalOutput) {
|
|
267
|
+
const { selectedUtxos, remainingUtxos } = utxo_1.utxoHelper.selectBtcUtxos(this._cacheBtcUtxos, totalOutput - totalInput);
|
|
268
|
+
if (selectedUtxos.length == 0) {
|
|
269
|
+
throw new error_1.WalletUtilsError(error_1.ErrorCodes.INSUFFICIENT_BTC_UTXO);
|
|
270
|
+
}
|
|
271
|
+
selectedUtxos.forEach((v) => {
|
|
272
|
+
this.addInput(v);
|
|
273
|
+
this._cacheToSignInputs.push({
|
|
274
|
+
index: this.inputs.length - 1,
|
|
275
|
+
publicKey: v.pubkey
|
|
276
|
+
});
|
|
277
|
+
this._cacheNetworkFee += utxo_1.utxoHelper.getAddedVirtualSize(v.addressType) * this.feeRate;
|
|
278
|
+
});
|
|
279
|
+
this._cacheBtcUtxos = remainingUtxos;
|
|
280
|
+
this.selectBtcUtxos();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
addSufficientUtxosForFee(btcUtxos, forceAsFee) {
|
|
284
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
285
|
+
if (btcUtxos.length > 0) {
|
|
286
|
+
this._cacheBtcUtxos = btcUtxos;
|
|
287
|
+
const dummyBtcUtxo = Object.assign({}, btcUtxos[0]);
|
|
288
|
+
dummyBtcUtxo.satoshis = 2100000000000000;
|
|
289
|
+
this.addInput(dummyBtcUtxo);
|
|
290
|
+
this.addChangeOutput(0);
|
|
291
|
+
const networkFee = yield this.calNetworkFee();
|
|
292
|
+
const dummyBtcUtxoSize = utxo_1.utxoHelper.getAddedVirtualSize(dummyBtcUtxo.addressType);
|
|
293
|
+
this._cacheNetworkFee = networkFee - dummyBtcUtxoSize * this.feeRate;
|
|
294
|
+
this.removeLastInput();
|
|
295
|
+
this.selectBtcUtxos();
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
if (forceAsFee) {
|
|
299
|
+
throw new error_1.WalletUtilsError(error_1.ErrorCodes.INSUFFICIENT_BTC_UTXO);
|
|
300
|
+
}
|
|
301
|
+
if (this.getTotalInput() < this.getTotalOutput()) {
|
|
302
|
+
throw new error_1.WalletUtilsError(error_1.ErrorCodes.INSUFFICIENT_BTC_UTXO);
|
|
303
|
+
}
|
|
304
|
+
this._cacheNetworkFee = yield this.calNetworkFee();
|
|
305
|
+
}
|
|
306
|
+
const changeAmount = this.getTotalInput() - this.getTotalOutput() - Math.ceil(this._cacheNetworkFee);
|
|
307
|
+
if (changeAmount > constants_1.UTXO_DUST) {
|
|
308
|
+
this.removeChangeOutput();
|
|
309
|
+
this.addChangeOutput(changeAmount);
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
this.removeChangeOutput();
|
|
313
|
+
}
|
|
314
|
+
return this._cacheToSignInputs;
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
dumpTx(psbt) {
|
|
318
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
319
|
+
const tx = psbt.extractTransaction();
|
|
320
|
+
const feeRate = psbt.getFeeRate();
|
|
321
|
+
console.log(`
|
|
322
|
+
=============================================================================================
|
|
323
|
+
Summary
|
|
324
|
+
txid: ${tx.getId()}
|
|
325
|
+
Size: ${tx.byteLength()}
|
|
326
|
+
Fee Paid: ${psbt.getFee()}
|
|
327
|
+
Fee Rate: ${feeRate} sat/vB
|
|
328
|
+
Detail: ${psbt.txInputs.length} Inputs, ${psbt.txOutputs.length} Outputs
|
|
329
|
+
----------------------------------------------------------------------------------------------
|
|
330
|
+
Inputs
|
|
331
|
+
${this.inputs
|
|
332
|
+
.map((input, index) => {
|
|
333
|
+
const str = `
|
|
334
|
+
=>${index} ${input.data.witnessUtxo.value} Sats
|
|
335
|
+
lock-size: ${input.data.witnessUtxo.script.length}
|
|
336
|
+
via ${input.data.hash} [${input.data.index}]
|
|
337
|
+
`;
|
|
338
|
+
return str;
|
|
339
|
+
})
|
|
340
|
+
.join('')}
|
|
341
|
+
total: ${this.getTotalInput()} Sats
|
|
342
|
+
----------------------------------------------------------------------------------------------
|
|
343
|
+
Outputs
|
|
344
|
+
${this.outputs
|
|
345
|
+
.map((output, index) => {
|
|
346
|
+
const str = `
|
|
347
|
+
=>${index} ${output.address} ${output.value} Sats`;
|
|
348
|
+
return str;
|
|
349
|
+
})
|
|
350
|
+
.join('')}
|
|
351
|
+
|
|
352
|
+
total: ${this.getTotalOutput()} Sats
|
|
353
|
+
=============================================================================================
|
|
354
|
+
`);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
exports.Transaction = Transaction;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { NetworkType } from '../network';
|
|
2
|
+
import { AddressType, UnspentOutput } from '../types';
|
|
3
|
+
declare function hasInscription(utxos: UnspentOutput[]): boolean;
|
|
4
|
+
declare function hasAtomicalsFT(utxos: UnspentOutput[]): boolean;
|
|
5
|
+
declare function hasAtomicalsNFT(utxos: UnspentOutput[]): boolean;
|
|
6
|
+
declare function hasAtomicals(utxos: UnspentOutput[]): boolean;
|
|
7
|
+
declare function hasAnyAssets(utxos: UnspentOutput[]): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* select utxos so that the total amount of utxos is greater than or equal to targetAmount
|
|
10
|
+
* return the selected utxos and the unselected utxos
|
|
11
|
+
* @param utxos
|
|
12
|
+
* @param targetAmount
|
|
13
|
+
*/
|
|
14
|
+
declare function selectBtcUtxos(utxos: UnspentOutput[], targetAmount: number): {
|
|
15
|
+
selectedUtxos: UnspentOutput[];
|
|
16
|
+
remainingUtxos: UnspentOutput[];
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* return the added virtual size of the utxo
|
|
20
|
+
*/
|
|
21
|
+
declare function getAddedVirtualSize(addressType: AddressType): number;
|
|
22
|
+
export declare function getUtxoDust(addressType: AddressType): 546 | 294 | 330;
|
|
23
|
+
export declare function getAddressUtxoDust(address: string, networkType?: NetworkType): number;
|
|
24
|
+
export declare const utxoHelper: {
|
|
25
|
+
hasAtomicalsFT: typeof hasAtomicalsFT;
|
|
26
|
+
hasAtomicalsNFT: typeof hasAtomicalsNFT;
|
|
27
|
+
hasAtomicals: typeof hasAtomicals;
|
|
28
|
+
hasInscription: typeof hasInscription;
|
|
29
|
+
hasAnyAssets: typeof hasAnyAssets;
|
|
30
|
+
selectBtcUtxos: typeof selectBtcUtxos;
|
|
31
|
+
getAddedVirtualSize: typeof getAddedVirtualSize;
|
|
32
|
+
getUtxoDust: typeof getUtxoDust;
|
|
33
|
+
getAddressUtxoDust: typeof getAddressUtxoDust;
|
|
34
|
+
};
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.utxoHelper = exports.getAddressUtxoDust = exports.getUtxoDust = void 0;
|
|
4
|
+
const address_1 = require("../address");
|
|
5
|
+
const network_1 = require("../network");
|
|
6
|
+
const types_1 = require("../types");
|
|
7
|
+
function hasInscription(utxos) {
|
|
8
|
+
if (utxos.find((v) => v.inscriptions.length > 0)) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
function hasAtomicalsFT(utxos) {
|
|
14
|
+
if (utxos.find((v) => v.atomicals.find((w) => w.type === 'FT'))) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
function hasAtomicalsNFT(utxos) {
|
|
20
|
+
if (utxos.find((v) => v.atomicals.find((w) => w.type === 'NFT'))) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
function hasAtomicals(utxos) {
|
|
26
|
+
if (utxos.find((v) => v.atomicals.length > 0)) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
function hasAnyAssets(utxos) {
|
|
32
|
+
if (utxos.find((v) => v.inscriptions.length > 0 || v.atomicals.length > 0)) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* select utxos so that the total amount of utxos is greater than or equal to targetAmount
|
|
39
|
+
* return the selected utxos and the unselected utxos
|
|
40
|
+
* @param utxos
|
|
41
|
+
* @param targetAmount
|
|
42
|
+
*/
|
|
43
|
+
function selectBtcUtxos(utxos, targetAmount) {
|
|
44
|
+
let selectedUtxos = [];
|
|
45
|
+
let remainingUtxos = [];
|
|
46
|
+
let totalAmount = 0;
|
|
47
|
+
for (const utxo of utxos) {
|
|
48
|
+
if (totalAmount < targetAmount) {
|
|
49
|
+
totalAmount += utxo.satoshis;
|
|
50
|
+
selectedUtxos.push(utxo);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
remainingUtxos.push(utxo);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
selectedUtxos,
|
|
58
|
+
remainingUtxos
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* return the added virtual size of the utxo
|
|
63
|
+
*/
|
|
64
|
+
function getAddedVirtualSize(addressType) {
|
|
65
|
+
if (addressType === types_1.AddressType.P2WPKH || addressType === types_1.AddressType.M44_P2WPKH) {
|
|
66
|
+
return 41 + (1 + 1 + 72 + 1 + 33) / 4;
|
|
67
|
+
}
|
|
68
|
+
else if (addressType === types_1.AddressType.P2TR || addressType === types_1.AddressType.M44_P2TR) {
|
|
69
|
+
return 41 + (1 + 1 + 64) / 4;
|
|
70
|
+
}
|
|
71
|
+
else if (addressType === types_1.AddressType.P2PKH) {
|
|
72
|
+
return 41 + 1 + 1 + 72 + 1 + 33;
|
|
73
|
+
}
|
|
74
|
+
else if (addressType === types_1.AddressType.P2SH_P2WPKH) {
|
|
75
|
+
return 41 + 24 + (1 + 1 + 72 + 1 + 33) / 4;
|
|
76
|
+
}
|
|
77
|
+
throw new Error('unknown address type');
|
|
78
|
+
}
|
|
79
|
+
function getUtxoDust(addressType) {
|
|
80
|
+
if (addressType === types_1.AddressType.P2WPKH || addressType === types_1.AddressType.M44_P2WPKH) {
|
|
81
|
+
return 294;
|
|
82
|
+
}
|
|
83
|
+
else if (addressType === types_1.AddressType.P2TR || addressType === types_1.AddressType.M44_P2TR) {
|
|
84
|
+
return 330;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
return 546;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.getUtxoDust = getUtxoDust;
|
|
91
|
+
// deprecated
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
93
|
+
function getAddressUtxoDust(address, networkType = network_1.NetworkType.MAINNET) {
|
|
94
|
+
return (0, address_1.decodeAddress)(address).dust;
|
|
95
|
+
}
|
|
96
|
+
exports.getAddressUtxoDust = getAddressUtxoDust;
|
|
97
|
+
exports.utxoHelper = {
|
|
98
|
+
hasAtomicalsFT,
|
|
99
|
+
hasAtomicalsNFT,
|
|
100
|
+
hasAtomicals,
|
|
101
|
+
hasInscription,
|
|
102
|
+
hasAnyAssets,
|
|
103
|
+
selectBtcUtxos,
|
|
104
|
+
getAddedVirtualSize,
|
|
105
|
+
getUtxoDust,
|
|
106
|
+
getAddressUtxoDust
|
|
107
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { sendAtomicalsFT } from './send-atomicals-ft';
|
|
2
|
+
import { sendAtomicalsNFT } from './send-atomicals-nft';
|
|
3
|
+
import { sendAllBTC, sendBTC } from './send-btc';
|
|
4
|
+
import { sendInscription } from './send-inscription';
|
|
5
|
+
import { sendInscriptions } from './send-inscriptions';
|
|
6
|
+
import { sendRunes } from './send-runes';
|
|
7
|
+
import { splitInscriptionUtxo } from './split-inscription-utxo';
|
|
8
|
+
export { sendAllBTC, sendAtomicalsFT, sendAtomicalsNFT, sendBTC, sendInscription, sendInscriptions, sendRunes, splitInscriptionUtxo };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.splitInscriptionUtxo = exports.sendRunes = exports.sendInscriptions = exports.sendInscription = exports.sendBTC = exports.sendAtomicalsNFT = exports.sendAtomicalsFT = exports.sendAllBTC = void 0;
|
|
4
|
+
const send_atomicals_ft_1 = require("./send-atomicals-ft");
|
|
5
|
+
Object.defineProperty(exports, "sendAtomicalsFT", { enumerable: true, get: function () { return send_atomicals_ft_1.sendAtomicalsFT; } });
|
|
6
|
+
const send_atomicals_nft_1 = require("./send-atomicals-nft");
|
|
7
|
+
Object.defineProperty(exports, "sendAtomicalsNFT", { enumerable: true, get: function () { return send_atomicals_nft_1.sendAtomicalsNFT; } });
|
|
8
|
+
const send_btc_1 = require("./send-btc");
|
|
9
|
+
Object.defineProperty(exports, "sendAllBTC", { enumerable: true, get: function () { return send_btc_1.sendAllBTC; } });
|
|
10
|
+
Object.defineProperty(exports, "sendBTC", { enumerable: true, get: function () { return send_btc_1.sendBTC; } });
|
|
11
|
+
const send_inscription_1 = require("./send-inscription");
|
|
12
|
+
Object.defineProperty(exports, "sendInscription", { enumerable: true, get: function () { return send_inscription_1.sendInscription; } });
|
|
13
|
+
const send_inscriptions_1 = require("./send-inscriptions");
|
|
14
|
+
Object.defineProperty(exports, "sendInscriptions", { enumerable: true, get: function () { return send_inscriptions_1.sendInscriptions; } });
|
|
15
|
+
const send_runes_1 = require("./send-runes");
|
|
16
|
+
Object.defineProperty(exports, "sendRunes", { enumerable: true, get: function () { return send_runes_1.sendRunes; } });
|
|
17
|
+
const split_inscription_utxo_1 = require("./split-inscription-utxo");
|
|
18
|
+
Object.defineProperty(exports, "splitInscriptionUtxo", { enumerable: true, get: function () { return split_inscription_utxo_1.splitInscriptionUtxo; } });
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { NetworkType } from '../network';
|
|
2
|
+
import { ToSignInput, UnspentOutput } from '../types';
|
|
3
|
+
export declare function sendAtomicalsFT(params: {
|
|
4
|
+
assetUtxos: UnspentOutput[];
|
|
5
|
+
btcUtxos: UnspentOutput[];
|
|
6
|
+
toAddress: string;
|
|
7
|
+
networkType: NetworkType;
|
|
8
|
+
changeAssetAddress: string;
|
|
9
|
+
sendAmount: number;
|
|
10
|
+
changeAddress: string;
|
|
11
|
+
feeRate: number;
|
|
12
|
+
enableRBF?: boolean;
|
|
13
|
+
}): Promise<{
|
|
14
|
+
psbt: import("bitcoinjs-lib").Psbt;
|
|
15
|
+
toSignInputs: ToSignInput[];
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.sendAtomicalsFT = void 0;
|
|
13
|
+
const error_1 = require("../error");
|
|
14
|
+
const transaction_1 = require("../transaction");
|
|
15
|
+
// only one arc20 can be send
|
|
16
|
+
function sendAtomicalsFT(params) {
|
|
17
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18
|
+
const { assetUtxos, btcUtxos, toAddress, networkType, changeAssetAddress, sendAmount, changeAddress, feeRate, enableRBF = true } = params;
|
|
19
|
+
// safe check
|
|
20
|
+
if (transaction_1.utxoHelper.hasAtomicalsNFT(assetUtxos) || transaction_1.utxoHelper.hasInscription(assetUtxos)) {
|
|
21
|
+
throw new error_1.WalletUtilsError(error_1.ErrorCodes.NOT_SAFE_UTXOS);
|
|
22
|
+
}
|
|
23
|
+
if (transaction_1.utxoHelper.hasAnyAssets(btcUtxos)) {
|
|
24
|
+
throw new error_1.WalletUtilsError(error_1.ErrorCodes.NOT_SAFE_UTXOS);
|
|
25
|
+
}
|
|
26
|
+
const tx = new transaction_1.Transaction();
|
|
27
|
+
tx.setNetworkType(networkType);
|
|
28
|
+
tx.setFeeRate(feeRate);
|
|
29
|
+
tx.setEnableRBF(enableRBF);
|
|
30
|
+
tx.setChangeAddress(changeAddress);
|
|
31
|
+
const toSignInputs = [];
|
|
32
|
+
let totalInputFTAmount = 0;
|
|
33
|
+
assetUtxos.forEach((v) => {
|
|
34
|
+
if (v.atomicals.length > 1) {
|
|
35
|
+
throw new error_1.WalletUtilsError(error_1.ErrorCodes.ONLY_ONE_ARC20_CAN_BE_SENT);
|
|
36
|
+
}
|
|
37
|
+
v.atomicals.forEach((w) => {
|
|
38
|
+
if (!w.atomicalValue) {
|
|
39
|
+
w.atomicalValue = v.satoshis;
|
|
40
|
+
}
|
|
41
|
+
totalInputFTAmount += w.atomicalValue;
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
if (sendAmount > totalInputFTAmount) {
|
|
45
|
+
throw new error_1.WalletUtilsError(error_1.ErrorCodes.INSUFFICIENT_ASSET_UTXO);
|
|
46
|
+
}
|
|
47
|
+
// add assets
|
|
48
|
+
assetUtxos.forEach((v, index) => {
|
|
49
|
+
tx.addInput(v);
|
|
50
|
+
toSignInputs.push({ index, publicKey: v.pubkey });
|
|
51
|
+
});
|
|
52
|
+
// add receiver
|
|
53
|
+
tx.addOutput(toAddress, sendAmount);
|
|
54
|
+
// add change
|
|
55
|
+
const changeArc20Amount = totalInputFTAmount - sendAmount;
|
|
56
|
+
if (changeArc20Amount > 0) {
|
|
57
|
+
tx.addOutput(changeAssetAddress, changeArc20Amount);
|
|
58
|
+
}
|
|
59
|
+
// add btc
|
|
60
|
+
const _toSignInputs = yield tx.addSufficientUtxosForFee(btcUtxos, true);
|
|
61
|
+
toSignInputs.push(..._toSignInputs);
|
|
62
|
+
const psbt = tx.toPsbt();
|
|
63
|
+
return { psbt, toSignInputs };
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
exports.sendAtomicalsFT = sendAtomicalsFT;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { NetworkType } from '../network';
|
|
2
|
+
import { ToSignInput, UnspentOutput } from '../types';
|
|
3
|
+
export declare function sendAtomicalsNFT(params: {
|
|
4
|
+
assetUtxo: UnspentOutput;
|
|
5
|
+
btcUtxos: UnspentOutput[];
|
|
6
|
+
toAddress: string;
|
|
7
|
+
networkType: NetworkType;
|
|
8
|
+
changeAddress: string;
|
|
9
|
+
feeRate: number;
|
|
10
|
+
enableRBF?: boolean;
|
|
11
|
+
}): Promise<{
|
|
12
|
+
psbt: import("bitcoinjs-lib").Psbt;
|
|
13
|
+
toSignInputs: ToSignInput[];
|
|
14
|
+
}>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.sendAtomicalsNFT = void 0;
|
|
13
|
+
const error_1 = require("../error");
|
|
14
|
+
const transaction_1 = require("../transaction");
|
|
15
|
+
function sendAtomicalsNFT(params) {
|
|
16
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
+
const { assetUtxo, btcUtxos, toAddress, networkType, changeAddress, feeRate, enableRBF = true } = params;
|
|
18
|
+
// safe check
|
|
19
|
+
if (transaction_1.utxoHelper.hasAtomicalsFT([assetUtxo]) || transaction_1.utxoHelper.hasInscription([assetUtxo])) {
|
|
20
|
+
throw new error_1.WalletUtilsError(error_1.ErrorCodes.NOT_SAFE_UTXOS);
|
|
21
|
+
}
|
|
22
|
+
if (transaction_1.utxoHelper.hasAnyAssets(btcUtxos)) {
|
|
23
|
+
throw new error_1.WalletUtilsError(error_1.ErrorCodes.NOT_SAFE_UTXOS);
|
|
24
|
+
}
|
|
25
|
+
if (assetUtxo.atomicals.length !== 1) {
|
|
26
|
+
throw new error_1.WalletUtilsError(error_1.ErrorCodes.NOT_SAFE_UTXOS);
|
|
27
|
+
}
|
|
28
|
+
const tx = new transaction_1.Transaction();
|
|
29
|
+
tx.setNetworkType(networkType);
|
|
30
|
+
tx.setFeeRate(feeRate);
|
|
31
|
+
tx.setEnableRBF(enableRBF);
|
|
32
|
+
tx.setChangeAddress(changeAddress);
|
|
33
|
+
const toSignInputs = [];
|
|
34
|
+
// add asset
|
|
35
|
+
tx.addInput(assetUtxo);
|
|
36
|
+
toSignInputs.push({ index: 0, publicKey: assetUtxo.pubkey });
|
|
37
|
+
tx.addOutput(toAddress, assetUtxo.satoshis);
|
|
38
|
+
// add btc
|
|
39
|
+
const _toSignInputs = yield tx.addSufficientUtxosForFee(btcUtxos, true);
|
|
40
|
+
toSignInputs.push(..._toSignInputs);
|
|
41
|
+
const psbt = tx.toPsbt();
|
|
42
|
+
return { psbt, toSignInputs };
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
exports.sendAtomicalsNFT = sendAtomicalsNFT;
|